prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Catch and handle EmptyResultSet error when trying to cache metadata labels
  EmptyResultSet
  
  Sentry Issue: [KOLIBRI-BACKEND-2E9](https://learningequality.sentry.io/issues/4005137733/?referrer=github_integration)
  
  ```
  EmptyResultSet: 
  (16 additional frame(s) were not displayed)
  ...
    File "django/db/models/sql/query.py", line 233, in __str__
      sql, params = self.sql_with_params()
    File "django/db/models/sql/query.py", line 241, in sql_with_params
      return self.get_compiler(DEFAULT_DB_ALIAS).as_sql()
    File "django/db/models/sql/compiler.py", line 441, in as_sql
      where, w_params = self.compile(self.where) if self.where is not None else ("", [])
    File "django/db/models/sql/compiler.py", line 373, in compile
      sql, params = node.as_sql(self, self.connection)
    File "django/db/models/sql/where.py", line 97, in as_sql
      raise EmptyResultSet
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `kolibri/core/content/utils/search.py`
  Content:
  ```
  1 """
  2 Avoiding direct model imports in here so that we can import these functions into places
  3 that should not initiate the Django app registry.
  4 """
  5 import hashlib
  6 
  7 try:
  8     from django.contrib.postgres.aggregates import BitOr
  9 except ImportError:
  10     BitOr = None
  11 
  12 from django.db import connections
  13 from django.db.models import Aggregate
  14 from django.db.models import Case
  15 from django.db.models import Value
  16 from django.db.models import When
  17 from django.db.models.fields import IntegerField
  18 from le_utils.constants.labels.accessibility_categories import (
  19     ACCESSIBILITYCATEGORIESLIST,
  20 )
  21 from le_utils.constants.labels.learning_activities import LEARNINGACTIVITIESLIST
  22 from le_utils.constants.labels.levels import LEVELSLIST
  23 from le_utils.constants.labels.needs import NEEDSLIST
  24 from le_utils.constants.labels.subjects import SUBJECTSLIST
  25 
  26 from kolibri.core.utils.cache import process_cache as cache
  27 
  28 
  29 metadata_lookup = {
  30     "learning_activities": LEARNINGACTIVITIESLIST,
  31     "categories": SUBJECTSLIST,
  32     "grade_levels": LEVELSLIST,
  33     "accessibility_labels": ACCESSIBILITYCATEGORIESLIST,
  34     "learner_needs": NEEDSLIST,
  35 }
  36 
  37 
  38 metadata_bitmasks = {}
  39 
  40 bitmask_fieldnames = {}
  41 
  42 
  43 for key, labels in metadata_lookup.items():
  44     bitmask_lookup = {}
  45     i = 0
  46     while labels[i : i + 64]:
  47         bitmask_field_name = "{}_bitmask_{}".format(key, i)
  48         bitmask_fieldnames[bitmask_field_name] = []
  49         for j, label in enumerate(labels):
  50             info = {
  51                 "bitmask_field_name": bitmask_field_name,
  52                 "field_name": key,
  53                 "bits": 2 ** j,
  54                 "label": label,
  55             }
  56             bitmask_lookup[label] = info
  57             bitmask_fieldnames[bitmask_field_name].append(info)
  58         i += 64
  59     metadata_bitmasks[key] = bitmask_lookup
  60 
  61 
  62 def _get_available_languages(base_queryset):
  63     from kolibri.core.content.models import Language
  64 
  65     langs = Language.objects.filter(
  66         id__in=base_queryset.exclude(lang=None)
  67         .values_list("lang_id", flat=True)
  68         .distinct()
  69     ).values("id", "lang_name")
  70     return list(langs)
  71 
  72 
  73 def _get_available_channels(base_queryset):
  74     from kolibri.core.content.models import ChannelMetadata
  75 
  76     return list(
  77         ChannelMetadata.objects.filter(
  78             id__in=base_queryset.values_list("channel_id", flat=True).distinct()
  79         ).values("id", "name")
  80     )
  81 
  82 
  83 class SQLiteBitwiseORAggregate(Aggregate):
  84     name = "BitwiseOR"
  85 
  86     def __init__(self, expression, num_bits=None, **extra):
  87         if not num_bits:
  88             raise ValueError("num_bits must be a positive integer")
  89         self.num_bits = num_bits
  90         super(SQLiteBitwiseORAggregate, self).__init__(
  91             expression, output_field=IntegerField(), **extra
  92         )
  93 
  94     @property
  95     def template(self):
  96         return " + ".join(
  97             "max(%(expressions)s&{})".format(2 ** i) for i in range(0, self.num_bits)
  98         )
  99 
  100 
  101 def get_available_metadata_labels(base_queryset):
  102     from kolibri.core.device.models import ContentCacheKey
  103 
  104     content_cache_key = ContentCacheKey.get_cache_key()
  105     cache_key = "search-labels:{}:{}".format(
  106         content_cache_key,
  107         hashlib.md5(str(base_queryset.query).encode("utf8")).hexdigest(),
  108     )
  109     if cache_key not in cache:
  110         base_queryset = base_queryset.order_by()
  111         aggregates = {}
  112         for field in bitmask_fieldnames:
  113             field_agg = field + "_agg"
  114             if connections[base_queryset.db].vendor == "sqlite" or BitOr is None:
  115                 aggregates[field_agg] = SQLiteBitwiseORAggregate(
  116                     field, num_bits=len(bitmask_fieldnames[field])
  117                 )
  118             elif connections[base_queryset.db].vendor == "postgresql":
  119                 aggregates[field_agg] = BitOr(field)
  120         output = {}
  121         agg = base_queryset.aggregate(**aggregates)
  122         for field, values in bitmask_fieldnames.items():
  123             bit_value = agg[field + "_agg"]
  124             for value in values:
  125                 if value["field_name"] not in output:
  126                     output[value["field_name"]] = []
  127                 if bit_value is not None and bit_value & value["bits"]:
  128                     output[value["field_name"]].append(value["label"])
  129         output["languages"] = _get_available_languages(base_queryset)
  130         output["channels"] = _get_available_channels(base_queryset)
  131         cache.set(cache_key, output, timeout=None)
  132     return cache.get(cache_key)
  133 
  134 
  135 def get_all_contentnode_label_metadata():
  136     from kolibri.core.content.models import ContentNode
  137 
  138     return get_available_metadata_labels(ContentNode.objects.filter(available=True))
  139 
  140 
  141 def annotate_label_bitmasks(queryset):
  142     update_statements = {}
  143     for bitmask_fieldname, label_info in bitmask_fieldnames.items():
  144         update_statements[bitmask_fieldname] = sum(
  145             Case(
  146                 When(
  147                     **{
  148                         info["field_name"] + "__contains": info["label"],
  149                         "then": Value(info["bits"]),
  150                     }
  151                 ),
  152                 default=Value(0),
  153             )
  154             for info in label_info
  155         )
  156     queryset.update(**update_statements)
  157
  ```
  --- 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/kolibri/core/content/utils/search.py b/kolibri/core/content/utils/search.py
  --- a/kolibri/core/content/utils/search.py
  +++ b/kolibri/core/content/utils/search.py
  @@ -9,6 +9,7 @@
   except ImportError:
       BitOr = None
   
  +from django.core.exceptions import EmptyResultSet
   from django.db import connections
   from django.db.models import Aggregate
   from django.db.models import Case
  @@ -39,6 +40,11 @@
   
   bitmask_fieldnames = {}
   
  +empty_labels = {
  +    "languages": [],
  +    "channels": [],
  +}
  +
   
   for key, labels in metadata_lookup.items():
       bitmask_lookup = {}
  @@ -57,6 +63,7 @@
               bitmask_fieldnames[bitmask_field_name].append(info)
           i += 64
       metadata_bitmasks[key] = bitmask_lookup
  +    empty_labels[key] = []
   
   
   def _get_available_languages(base_queryset):
  @@ -98,14 +105,17 @@
           )
   
   
  -def get_available_metadata_labels(base_queryset):
  +def get_available_metadata_labels(base_queryset):  # noqa: C901
       from kolibri.core.device.models import ContentCacheKey
   
       content_cache_key = ContentCacheKey.get_cache_key()
  -    cache_key = "search-labels:{}:{}".format(
  -        content_cache_key,
  -        hashlib.md5(str(base_queryset.query).encode("utf8")).hexdigest(),
  -    )
  +    try:
  +        cache_key = "search-labels:{}:{}".format(
  +            content_cache_key,
  +            hashlib.md5(str(base_queryset.query).encode("utf8")).hexdigest(),
  +        )
  +    except EmptyResultSet:
  +        return empty_labels
       if cache_key not in cache:
           base_queryset = base_queryset.order_by()
           aggregates = {}
  
verification_info: {"golden_diff": "diff --git a/kolibri/core/content/utils/search.py b/kolibri/core/content/utils/search.py\n--- a/kolibri/core/content/utils/search.py\n+++ b/kolibri/core/content/utils/search.py\n@@ -9,6 +9,7 @@\n except ImportError:\n     BitOr = None\n \n+from django.core.exceptions import EmptyResultSet\n from django.db import connections\n from django.db.models import Aggregate\n from django.db.models import Case\n@@ -39,6 +40,11 @@\n \n bitmask_fieldnames = {}\n \n+empty_labels = {\n+    \"languages\": [],\n+    \"channels\": [],\n+}\n+\n \n for key, labels in metadata_lookup.items():\n     bitmask_lookup = {}\n@@ -57,6 +63,7 @@\n             bitmask_fieldnames[bitmask_field_name].append(info)\n         i += 64\n     metadata_bitmasks[key] = bitmask_lookup\n+    empty_labels[key] = []\n \n \n def _get_available_languages(base_queryset):\n@@ -98,14 +105,17 @@\n         )\n \n \n-def get_available_metadata_labels(base_queryset):\n+def get_available_metadata_labels(base_queryset):  # noqa: C901\n     from kolibri.core.device.models import ContentCacheKey\n \n     content_cache_key = ContentCacheKey.get_cache_key()\n-    cache_key = \"search-labels:{}:{}\".format(\n-        content_cache_key,\n-        hashlib.md5(str(base_queryset.query).encode(\"utf8\")).hexdigest(),\n-    )\n+    try:\n+        cache_key = \"search-labels:{}:{}\".format(\n+            content_cache_key,\n+            hashlib.md5(str(base_queryset.query).encode(\"utf8\")).hexdigest(),\n+        )\n+    except EmptyResultSet:\n+        return empty_labels\n     if cache_key not in cache:\n         base_queryset = base_queryset.order_by()\n         aggregates = {}\n", "issue": "Catch and handle EmptyResultSet error when trying to cache metadata labels\nEmptyResultSet\n\nSentry Issue: [KOLIBRI-BACKEND-2E9](https://learningequality.sentry.io/issues/4005137733/?referrer=github_integration)\n\n```\nEmptyResultSet: \n(16 additional frame(s) were not displayed)\n...\n  File \"django/db/models/sql/query.py\", line 233, in __str__\n    sql, params = self.sql_with_params()\n  File \"django/db/models/sql/query.py\", line 241, in sql_with_params\n    return self.get_compiler(DEFAULT_DB_ALIAS).as_sql()\n  File \"django/db/models/sql/compiler.py\", line 441, in as_sql\n    where, w_params = self.compile(self.where) if self.where is not None else (\"\", [])\n  File \"django/db/models/sql/compiler.py\", line 373, in compile\n    sql, params = node.as_sql(self, self.connection)\n  File \"django/db/models/sql/where.py\", line 97, in as_sql\n    raise EmptyResultSet\n```\n", "before_files": [{"content": "\"\"\"\nAvoiding direct model imports in here so that we can import these functions into places\nthat should not initiate the Django app registry.\n\"\"\"\nimport hashlib\n\ntry:\n    from django.contrib.postgres.aggregates import BitOr\nexcept ImportError:\n    BitOr = None\n\nfrom django.db import connections\nfrom django.db.models import Aggregate\nfrom django.db.models import Case\nfrom django.db.models import Value\nfrom django.db.models import When\nfrom django.db.models.fields import IntegerField\nfrom le_utils.constants.labels.accessibility_categories import (\n    ACCESSIBILITYCATEGORIESLIST,\n)\nfrom le_utils.constants.labels.learning_activities import LEARNINGACTIVITIESLIST\nfrom le_utils.constants.labels.levels import LEVELSLIST\nfrom le_utils.constants.labels.needs import NEEDSLIST\nfrom le_utils.constants.labels.subjects import SUBJECTSLIST\n\nfrom kolibri.core.utils.cache import process_cache as cache\n\n\nmetadata_lookup = {\n    \"learning_activities\": LEARNINGACTIVITIESLIST,\n    \"categories\": SUBJECTSLIST,\n    \"grade_levels\": LEVELSLIST,\n    \"accessibility_labels\": ACCESSIBILITYCATEGORIESLIST,\n    \"learner_needs\": NEEDSLIST,\n}\n\n\nmetadata_bitmasks = {}\n\nbitmask_fieldnames = {}\n\n\nfor key, labels in metadata_lookup.items():\n    bitmask_lookup = {}\n    i = 0\n    while labels[i : i + 64]:\n        bitmask_field_name = \"{}_bitmask_{}\".format(key, i)\n        bitmask_fieldnames[bitmask_field_name] = []\n        for j, label in enumerate(labels):\n            info = {\n                \"bitmask_field_name\": bitmask_field_name,\n                \"field_name\": key,\n                \"bits\": 2 ** j,\n                \"label\": label,\n            }\n            bitmask_lookup[label] = info\n            bitmask_fieldnames[bitmask_field_name].append(info)\n        i += 64\n    metadata_bitmasks[key] = bitmask_lookup\n\n\ndef _get_available_languages(base_queryset):\n    from kolibri.core.content.models import Language\n\n    langs = Language.objects.filter(\n        id__in=base_queryset.exclude(lang=None)\n        .values_list(\"lang_id\", flat=True)\n        .distinct()\n    ).values(\"id\", \"lang_name\")\n    return list(langs)\n\n\ndef _get_available_channels(base_queryset):\n    from kolibri.core.content.models import ChannelMetadata\n\n    return list(\n        ChannelMetadata.objects.filter(\n            id__in=base_queryset.values_list(\"channel_id\", flat=True).distinct()\n        ).values(\"id\", \"name\")\n    )\n\n\nclass SQLiteBitwiseORAggregate(Aggregate):\n    name = \"BitwiseOR\"\n\n    def __init__(self, expression, num_bits=None, **extra):\n        if not num_bits:\n            raise ValueError(\"num_bits must be a positive integer\")\n        self.num_bits = num_bits\n        super(SQLiteBitwiseORAggregate, self).__init__(\n            expression, output_field=IntegerField(), **extra\n        )\n\n    @property\n    def template(self):\n        return \" + \".join(\n            \"max(%(expressions)s&{})\".format(2 ** i) for i in range(0, self.num_bits)\n        )\n\n\ndef get_available_metadata_labels(base_queryset):\n    from kolibri.core.device.models import ContentCacheKey\n\n    content_cache_key = ContentCacheKey.get_cache_key()\n    cache_key = \"search-labels:{}:{}\".format(\n        content_cache_key,\n        hashlib.md5(str(base_queryset.query).encode(\"utf8\")).hexdigest(),\n    )\n    if cache_key not in cache:\n        base_queryset = base_queryset.order_by()\n        aggregates = {}\n        for field in bitmask_fieldnames:\n            field_agg = field + \"_agg\"\n            if connections[base_queryset.db].vendor == \"sqlite\" or BitOr is None:\n                aggregates[field_agg] = SQLiteBitwiseORAggregate(\n                    field, num_bits=len(bitmask_fieldnames[field])\n                )\n            elif connections[base_queryset.db].vendor == \"postgresql\":\n                aggregates[field_agg] = BitOr(field)\n        output = {}\n        agg = base_queryset.aggregate(**aggregates)\n        for field, values in bitmask_fieldnames.items():\n            bit_value = agg[field + \"_agg\"]\n            for value in values:\n                if value[\"field_name\"] not in output:\n                    output[value[\"field_name\"]] = []\n                if bit_value is not None and bit_value & value[\"bits\"]:\n                    output[value[\"field_name\"]].append(value[\"label\"])\n        output[\"languages\"] = _get_available_languages(base_queryset)\n        output[\"channels\"] = _get_available_channels(base_queryset)\n        cache.set(cache_key, output, timeout=None)\n    return cache.get(cache_key)\n\n\ndef get_all_contentnode_label_metadata():\n    from kolibri.core.content.models import ContentNode\n\n    return get_available_metadata_labels(ContentNode.objects.filter(available=True))\n\n\ndef annotate_label_bitmasks(queryset):\n    update_statements = {}\n    for bitmask_fieldname, label_info in bitmask_fieldnames.items():\n        update_statements[bitmask_fieldname] = sum(\n            Case(\n                When(\n                    **{\n                        info[\"field_name\"] + \"__contains\": info[\"label\"],\n                        \"then\": Value(info[\"bits\"]),\n                    }\n                ),\n                default=Value(0),\n            )\n            for info in label_info\n        )\n    queryset.update(**update_statements)\n", "path": "kolibri/core/content/utils/search.py"}], "after_files": [{"content": "\"\"\"\nAvoiding direct model imports in here so that we can import these functions into places\nthat should not initiate the Django app registry.\n\"\"\"\nimport hashlib\n\ntry:\n    from django.contrib.postgres.aggregates import BitOr\nexcept ImportError:\n    BitOr = None\n\nfrom django.core.exceptions import EmptyResultSet\nfrom django.db import connections\nfrom django.db.models import Aggregate\nfrom django.db.models import Case\nfrom django.db.models import Value\nfrom django.db.models import When\nfrom django.db.models.fields import IntegerField\nfrom le_utils.constants.labels.accessibility_categories import (\n    ACCESSIBILITYCATEGORIESLIST,\n)\nfrom le_utils.constants.labels.learning_activities import LEARNINGACTIVITIESLIST\nfrom le_utils.constants.labels.levels import LEVELSLIST\nfrom le_utils.constants.labels.needs import NEEDSLIST\nfrom le_utils.constants.labels.subjects import SUBJECTSLIST\n\nfrom kolibri.core.utils.cache import process_cache as cache\n\n\nmetadata_lookup = {\n    \"learning_activities\": LEARNINGACTIVITIESLIST,\n    \"categories\": SUBJECTSLIST,\n    \"grade_levels\": LEVELSLIST,\n    \"accessibility_labels\": ACCESSIBILITYCATEGORIESLIST,\n    \"learner_needs\": NEEDSLIST,\n}\n\n\nmetadata_bitmasks = {}\n\nbitmask_fieldnames = {}\n\nempty_labels = {\n    \"languages\": [],\n    \"channels\": [],\n}\n\n\nfor key, labels in metadata_lookup.items():\n    bitmask_lookup = {}\n    i = 0\n    while labels[i : i + 64]:\n        bitmask_field_name = \"{}_bitmask_{}\".format(key, i)\n        bitmask_fieldnames[bitmask_field_name] = []\n        for j, label in enumerate(labels):\n            info = {\n                \"bitmask_field_name\": bitmask_field_name,\n                \"field_name\": key,\n                \"bits\": 2 ** j,\n                \"label\": label,\n            }\n            bitmask_lookup[label] = info\n            bitmask_fieldnames[bitmask_field_name].append(info)\n        i += 64\n    metadata_bitmasks[key] = bitmask_lookup\n    empty_labels[key] = []\n\n\ndef _get_available_languages(base_queryset):\n    from kolibri.core.content.models import Language\n\n    langs = Language.objects.filter(\n        id__in=base_queryset.exclude(lang=None)\n        .values_list(\"lang_id\", flat=True)\n        .distinct()\n    ).values(\"id\", \"lang_name\")\n    return list(langs)\n\n\ndef _get_available_channels(base_queryset):\n    from kolibri.core.content.models import ChannelMetadata\n\n    return list(\n        ChannelMetadata.objects.filter(\n            id__in=base_queryset.values_list(\"channel_id\", flat=True).distinct()\n        ).values(\"id\", \"name\")\n    )\n\n\nclass SQLiteBitwiseORAggregate(Aggregate):\n    name = \"BitwiseOR\"\n\n    def __init__(self, expression, num_bits=None, **extra):\n        if not num_bits:\n            raise ValueError(\"num_bits must be a positive integer\")\n        self.num_bits = num_bits\n        super(SQLiteBitwiseORAggregate, self).__init__(\n            expression, output_field=IntegerField(), **extra\n        )\n\n    @property\n    def template(self):\n        return \" + \".join(\n            \"max(%(expressions)s&{})\".format(2 ** i) for i in range(0, self.num_bits)\n        )\n\n\ndef get_available_metadata_labels(base_queryset):  # noqa: C901\n    from kolibri.core.device.models import ContentCacheKey\n\n    content_cache_key = ContentCacheKey.get_cache_key()\n    try:\n        cache_key = \"search-labels:{}:{}\".format(\n            content_cache_key,\n            hashlib.md5(str(base_queryset.query).encode(\"utf8\")).hexdigest(),\n        )\n    except EmptyResultSet:\n        return empty_labels\n    if cache_key not in cache:\n        base_queryset = base_queryset.order_by()\n        aggregates = {}\n        for field in bitmask_fieldnames:\n            field_agg = field + \"_agg\"\n            if connections[base_queryset.db].vendor == \"sqlite\" or BitOr is None:\n                aggregates[field_agg] = SQLiteBitwiseORAggregate(\n                    field, num_bits=len(bitmask_fieldnames[field])\n                )\n            elif connections[base_queryset.db].vendor == \"postgresql\":\n                aggregates[field_agg] = BitOr(field)\n        output = {}\n        agg = base_queryset.aggregate(**aggregates)\n        for field, values in bitmask_fieldnames.items():\n            bit_value = agg[field + \"_agg\"]\n            for value in values:\n                if value[\"field_name\"] not in output:\n                    output[value[\"field_name\"]] = []\n                if bit_value is not None and bit_value & value[\"bits\"]:\n                    output[value[\"field_name\"]].append(value[\"label\"])\n        output[\"languages\"] = _get_available_languages(base_queryset)\n        output[\"channels\"] = _get_available_channels(base_queryset)\n        cache.set(cache_key, output, timeout=None)\n    return cache.get(cache_key)\n\n\ndef get_all_contentnode_label_metadata():\n    from kolibri.core.content.models import ContentNode\n\n    return get_available_metadata_labels(ContentNode.objects.filter(available=True))\n\n\ndef annotate_label_bitmasks(queryset):\n    update_statements = {}\n    for bitmask_fieldname, label_info in bitmask_fieldnames.items():\n        update_statements[bitmask_fieldname] = sum(\n            Case(\n                When(\n                    **{\n                        info[\"field_name\"] + \"__contains\": info[\"label\"],\n                        \"then\": Value(info[\"bits\"]),\n                    }\n                ),\n                default=Value(0),\n            )\n            for info in label_info\n        )\n    queryset.update(**update_statements)\n", "path": "kolibri/core/content/utils/search.py"}]}