prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add thematic labels to indicator
  The granular way of working with thematic labels attached to indicators is extremely prone to error at the FE due to the complexity of handling it, waiting for IDs assigned from backend for each label, etc. This will decrease UX as the component will have to freeze to wait for backend syncs and will break the normal pattern of auto-saving.
  In order to wrap this up properly we need to have a simpler way of editing the labels attached to indicator, namely as a simple list of label **values**:
  
  ```
  thematic_labels: [31, 17]
  ```
  
  This property would need to be added to the indicator and to allow GET & PATCH.
  
  --- 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/rest/filters.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 
  3 # Akvo Reporting is covered by the GNU Affero General Public License.
  4 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  5 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  6 
  7 import ast
  8 
  9 from django.db.models import Q
  10 from django.core.exceptions import FieldError
  11 
  12 from rest_framework import filters
  13 from rest_framework.exceptions import APIException
  14 
  15 
  16 class RSRGenericFilterBackend(filters.BaseFilterBackend):
  17 
  18     def filter_queryset(self, request, queryset, view):
  19         """
  20         Return a queryset possibly filtered by query param values.
  21         The filter looks for the query param keys filter and exclude
  22         For each of these query param the value is evaluated using ast.literal_eval() and used as
  23         kwargs in queryset.filter and queryset.exclude respectively.
  24 
  25         Example URLs:
  26             https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water','currency':'EUR'}
  27             https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water'}&exclude={'currency':'EUR'}
  28 
  29         It's also possible to specify models to be included in select_related() and
  30         prefetch_related() calls on the queryset, but specifying these in lists of strings as the
  31         values for the query sting params select_relates and prefetch_related.
  32 
  33         Example:
  34             https://rsr.akvo.org/rest/v1/project/?filter={'partners__in':[42,43]}&prefetch_related=['partners']
  35 
  36         Finally limited support for filtering on multiple arguments using logical OR between
  37         those expressions is available. To use this supply two or more query string keywords on the
  38         form q_filter1, q_filter2... where the value is a dict that can be used as a kwarg in a Q
  39         object. All those Q objects created are used in a queryset.filter() call concatenated using
  40         the | operator.
  41         """
  42         def eval_query_value(request, key):
  43             """
  44             Use ast.literal_eval() to evaluate a query string value as a python data type object
  45             :param request: the django request object
  46             :param param: the query string param key
  47             :return: a python data type object, or None if literal_eval() fails
  48             """
  49             value = request.query_params.get(key, None)
  50             try:
  51                 return ast.literal_eval(value)
  52             except (ValueError, SyntaxError):
  53                 return None
  54 
  55         qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related']
  56 
  57         # evaluate each query string param, and apply the queryset method with the same name
  58         for param in qs_params:
  59             args_or_kwargs = eval_query_value(request, param)
  60             if args_or_kwargs:
  61                 # filter and exclude are called with a dict kwarg, the _related methods with a list
  62                 try:
  63                     if param in ['filter', 'exclude', ]:
  64                         queryset = getattr(queryset, param)(**args_or_kwargs)
  65                     else:
  66                         queryset = getattr(queryset, param)(*args_or_kwargs)
  67 
  68                 except FieldError as e:
  69                     raise APIException("Error in request: {message}".format(message=e.message))
  70 
  71         # support for Q expressions, limited to OR-concatenated filtering
  72         if request.query_params.get('q_filter1', None):
  73             i = 1
  74             q_queries = []
  75             while request.query_params.get('q_filter{}'.format(i), None):
  76                 query_arg = eval_query_value(request, 'q_filter{}'.format(i))
  77                 if query_arg:
  78                     q_queries += [query_arg]
  79                 i += 1
  80 
  81             q_expr = Q(**q_queries[0])
  82             for query in q_queries[1:]:
  83                 q_expr = q_expr | Q(**query)
  84 
  85             queryset = queryset.filter(q_expr)
  86 
  87         return queryset
  88
  ```
  
  Path: `akvo/rest/serializers/indicator.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 
  3 # Akvo RSR is covered by the GNU Affero General Public License.
  4 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  5 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  6 
  7 from akvo.rest.serializers.indicator_period import (
  8     IndicatorPeriodFrameworkSerializer, IndicatorPeriodFrameworkLiteSerializer)
  9 from akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer
  10 from akvo.rest.serializers.rsr_serializer import BaseRSRSerializer
  11 from akvo.rsr.models import Indicator, IndicatorDimensionName
  12 
  13 from rest_framework import serializers
  14 
  15 
  16 class IndicatorSerializer(BaseRSRSerializer):
  17 
  18     result_unicode = serializers.ReadOnlyField(source='result.__unicode__')
  19     measure_label = serializers.ReadOnlyField(source='iati_measure_unicode')
  20     children_aggregate_percentage = serializers.ReadOnlyField()
  21     dimension_names = serializers.PrimaryKeyRelatedField(
  22         many=True, queryset=IndicatorDimensionName.objects.all())
  23 
  24     class Meta:
  25         model = Indicator
  26         fields = '__all__'
  27 
  28     # TODO: add validation for parent_indicator
  29 
  30 
  31 class IndicatorFrameworkSerializer(BaseRSRSerializer):
  32 
  33     periods = IndicatorPeriodFrameworkSerializer(many=True, required=False, read_only=True)
  34     parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')
  35     children_aggregate_percentage = serializers.ReadOnlyField()
  36     dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)
  37 
  38     class Meta:
  39         model = Indicator
  40         fields = '__all__'
  41 
  42 
  43 class IndicatorFrameworkLiteSerializer(BaseRSRSerializer):
  44 
  45     periods = IndicatorPeriodFrameworkLiteSerializer(many=True, required=False, read_only=True)
  46     parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')
  47     children_aggregate_percentage = serializers.ReadOnlyField()
  48     dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)
  49 
  50     class Meta:
  51         model = Indicator
  52         fields = '__all__'
  53
  ```
  --- 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/rest/filters.py b/akvo/rest/filters.py
  --- a/akvo/rest/filters.py
  +++ b/akvo/rest/filters.py
  @@ -84,4 +84,4 @@
   
               queryset = queryset.filter(q_expr)
   
  -        return queryset
  +        return queryset.distinct()
  diff --git a/akvo/rest/serializers/indicator.py b/akvo/rest/serializers/indicator.py
  --- a/akvo/rest/serializers/indicator.py
  +++ b/akvo/rest/serializers/indicator.py
  @@ -8,11 +8,29 @@
       IndicatorPeriodFrameworkSerializer, IndicatorPeriodFrameworkLiteSerializer)
   from akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer
   from akvo.rest.serializers.rsr_serializer import BaseRSRSerializer
  -from akvo.rsr.models import Indicator, IndicatorDimensionName
  +from akvo.rsr.models import Indicator, IndicatorDimensionName, IndicatorLabel
   
   from rest_framework import serializers
   
   
  +class LabelListingField(serializers.RelatedField):
  +
  +    def to_representation(self, labels):
  +        return list(labels.values_list('label_id', flat=True))
  +
  +    def to_internal_value(self, org_label_ids):
  +        indicator = self.root.instance
  +        existing_labels = set(indicator.labels.values_list('label_id', flat=True))
  +        new_labels = set(org_label_ids) - existing_labels
  +        deleted_labels = existing_labels - set(org_label_ids)
  +        labels = [IndicatorLabel(indicator=indicator, label_id=org_label_id) for org_label_id in new_labels]
  +        IndicatorLabel.objects.bulk_create(labels)
  +        if deleted_labels:
  +            IndicatorLabel.objects.filter(label_id__in=deleted_labels).delete()
  +
  +        return indicator.labels.all()
  +
  +
   class IndicatorSerializer(BaseRSRSerializer):
   
       result_unicode = serializers.ReadOnlyField(source='result.__unicode__')
  @@ -34,6 +52,7 @@
       parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')
       children_aggregate_percentage = serializers.ReadOnlyField()
       dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)
  +    labels = LabelListingField(queryset=IndicatorLabel.objects.all(), required=False)
   
       class Meta:
           model = Indicator
  @@ -46,6 +65,7 @@
       parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')
       children_aggregate_percentage = serializers.ReadOnlyField()
       dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)
  +    labels = LabelListingField(read_only=True)
   
       class Meta:
           model = Indicator
  
verification_info: {"golden_diff": "diff --git a/akvo/rest/filters.py b/akvo/rest/filters.py\n--- a/akvo/rest/filters.py\n+++ b/akvo/rest/filters.py\n@@ -84,4 +84,4 @@\n \n             queryset = queryset.filter(q_expr)\n \n-        return queryset\n+        return queryset.distinct()\ndiff --git a/akvo/rest/serializers/indicator.py b/akvo/rest/serializers/indicator.py\n--- a/akvo/rest/serializers/indicator.py\n+++ b/akvo/rest/serializers/indicator.py\n@@ -8,11 +8,29 @@\n     IndicatorPeriodFrameworkSerializer, IndicatorPeriodFrameworkLiteSerializer)\n from akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer\n from akvo.rest.serializers.rsr_serializer import BaseRSRSerializer\n-from akvo.rsr.models import Indicator, IndicatorDimensionName\n+from akvo.rsr.models import Indicator, IndicatorDimensionName, IndicatorLabel\n \n from rest_framework import serializers\n \n \n+class LabelListingField(serializers.RelatedField):\n+\n+    def to_representation(self, labels):\n+        return list(labels.values_list('label_id', flat=True))\n+\n+    def to_internal_value(self, org_label_ids):\n+        indicator = self.root.instance\n+        existing_labels = set(indicator.labels.values_list('label_id', flat=True))\n+        new_labels = set(org_label_ids) - existing_labels\n+        deleted_labels = existing_labels - set(org_label_ids)\n+        labels = [IndicatorLabel(indicator=indicator, label_id=org_label_id) for org_label_id in new_labels]\n+        IndicatorLabel.objects.bulk_create(labels)\n+        if deleted_labels:\n+            IndicatorLabel.objects.filter(label_id__in=deleted_labels).delete()\n+\n+        return indicator.labels.all()\n+\n+\n class IndicatorSerializer(BaseRSRSerializer):\n \n     result_unicode = serializers.ReadOnlyField(source='result.__unicode__')\n@@ -34,6 +52,7 @@\n     parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n     children_aggregate_percentage = serializers.ReadOnlyField()\n     dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n+    labels = LabelListingField(queryset=IndicatorLabel.objects.all(), required=False)\n \n     class Meta:\n         model = Indicator\n@@ -46,6 +65,7 @@\n     parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n     children_aggregate_percentage = serializers.ReadOnlyField()\n     dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n+    labels = LabelListingField(read_only=True)\n \n     class Meta:\n         model = Indicator\n", "issue": "Add thematic labels to indicator\nThe granular way of working with thematic labels attached to indicators is extremely prone to error at the FE due to the complexity of handling it, waiting for IDs assigned from backend for each label, etc. This will decrease UX as the component will have to freeze to wait for backend syncs and will break the normal pattern of auto-saving.\r\nIn order to wrap this up properly we need to have a simpler way of editing the labels attached to indicator, namely as a simple list of label **values**:\r\n\r\n```\r\nthematic_labels: [31, 17]\r\n```\r\n\r\nThis property would need to be added to the indicator and to allow GET & PATCH.\n", "before_files": [{"content": "# -*- 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\nimport ast\n\nfrom django.db.models import Q\nfrom django.core.exceptions import FieldError\n\nfrom rest_framework import filters\nfrom rest_framework.exceptions import APIException\n\n\nclass RSRGenericFilterBackend(filters.BaseFilterBackend):\n\n    def filter_queryset(self, request, queryset, view):\n        \"\"\"\n        Return a queryset possibly filtered by query param values.\n        The filter looks for the query param keys filter and exclude\n        For each of these query param the value is evaluated using ast.literal_eval() and used as\n        kwargs in queryset.filter and queryset.exclude respectively.\n\n        Example URLs:\n            https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water','currency':'EUR'}\n            https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water'}&exclude={'currency':'EUR'}\n\n        It's also possible to specify models to be included in select_related() and\n        prefetch_related() calls on the queryset, but specifying these in lists of strings as the\n        values for the query sting params select_relates and prefetch_related.\n\n        Example:\n            https://rsr.akvo.org/rest/v1/project/?filter={'partners__in':[42,43]}&prefetch_related=['partners']\n\n        Finally limited support for filtering on multiple arguments using logical OR between\n        those expressions is available. To use this supply two or more query string keywords on the\n        form q_filter1, q_filter2... where the value is a dict that can be used as a kwarg in a Q\n        object. All those Q objects created are used in a queryset.filter() call concatenated using\n        the | operator.\n        \"\"\"\n        def eval_query_value(request, key):\n            \"\"\"\n            Use ast.literal_eval() to evaluate a query string value as a python data type object\n            :param request: the django request object\n            :param param: the query string param key\n            :return: a python data type object, or None if literal_eval() fails\n            \"\"\"\n            value = request.query_params.get(key, None)\n            try:\n                return ast.literal_eval(value)\n            except (ValueError, SyntaxError):\n                return None\n\n        qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related']\n\n        # evaluate each query string param, and apply the queryset method with the same name\n        for param in qs_params:\n            args_or_kwargs = eval_query_value(request, param)\n            if args_or_kwargs:\n                # filter and exclude are called with a dict kwarg, the _related methods with a list\n                try:\n                    if param in ['filter', 'exclude', ]:\n                        queryset = getattr(queryset, param)(**args_or_kwargs)\n                    else:\n                        queryset = getattr(queryset, param)(*args_or_kwargs)\n\n                except FieldError as e:\n                    raise APIException(\"Error in request: {message}\".format(message=e.message))\n\n        # support for Q expressions, limited to OR-concatenated filtering\n        if request.query_params.get('q_filter1', None):\n            i = 1\n            q_queries = []\n            while request.query_params.get('q_filter{}'.format(i), None):\n                query_arg = eval_query_value(request, 'q_filter{}'.format(i))\n                if query_arg:\n                    q_queries += [query_arg]\n                i += 1\n\n            q_expr = Q(**q_queries[0])\n            for query in q_queries[1:]:\n                q_expr = q_expr | Q(**query)\n\n            queryset = queryset.filter(q_expr)\n\n        return queryset\n", "path": "akvo/rest/filters.py"}, {"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR 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\nfrom akvo.rest.serializers.indicator_period import (\n    IndicatorPeriodFrameworkSerializer, IndicatorPeriodFrameworkLiteSerializer)\nfrom akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer\nfrom akvo.rest.serializers.rsr_serializer import BaseRSRSerializer\nfrom akvo.rsr.models import Indicator, IndicatorDimensionName\n\nfrom rest_framework import serializers\n\n\nclass IndicatorSerializer(BaseRSRSerializer):\n\n    result_unicode = serializers.ReadOnlyField(source='result.__unicode__')\n    measure_label = serializers.ReadOnlyField(source='iati_measure_unicode')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = serializers.PrimaryKeyRelatedField(\n        many=True, queryset=IndicatorDimensionName.objects.all())\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n\n    # TODO: add validation for parent_indicator\n\n\nclass IndicatorFrameworkSerializer(BaseRSRSerializer):\n\n    periods = IndicatorPeriodFrameworkSerializer(many=True, required=False, read_only=True)\n    parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n\n\nclass IndicatorFrameworkLiteSerializer(BaseRSRSerializer):\n\n    periods = IndicatorPeriodFrameworkLiteSerializer(many=True, required=False, read_only=True)\n    parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n", "path": "akvo/rest/serializers/indicator.py"}], "after_files": [{"content": "# -*- 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\nimport ast\n\nfrom django.db.models import Q\nfrom django.core.exceptions import FieldError\n\nfrom rest_framework import filters\nfrom rest_framework.exceptions import APIException\n\n\nclass RSRGenericFilterBackend(filters.BaseFilterBackend):\n\n    def filter_queryset(self, request, queryset, view):\n        \"\"\"\n        Return a queryset possibly filtered by query param values.\n        The filter looks for the query param keys filter and exclude\n        For each of these query param the value is evaluated using ast.literal_eval() and used as\n        kwargs in queryset.filter and queryset.exclude respectively.\n\n        Example URLs:\n            https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water','currency':'EUR'}\n            https://rsr.akvo.org/rest/v1/project/?filter={'title__icontains':'water'}&exclude={'currency':'EUR'}\n\n        It's also possible to specify models to be included in select_related() and\n        prefetch_related() calls on the queryset, but specifying these in lists of strings as the\n        values for the query sting params select_relates and prefetch_related.\n\n        Example:\n            https://rsr.akvo.org/rest/v1/project/?filter={'partners__in':[42,43]}&prefetch_related=['partners']\n\n        Finally limited support for filtering on multiple arguments using logical OR between\n        those expressions is available. To use this supply two or more query string keywords on the\n        form q_filter1, q_filter2... where the value is a dict that can be used as a kwarg in a Q\n        object. All those Q objects created are used in a queryset.filter() call concatenated using\n        the | operator.\n        \"\"\"\n        def eval_query_value(request, key):\n            \"\"\"\n            Use ast.literal_eval() to evaluate a query string value as a python data type object\n            :param request: the django request object\n            :param param: the query string param key\n            :return: a python data type object, or None if literal_eval() fails\n            \"\"\"\n            value = request.query_params.get(key, None)\n            try:\n                return ast.literal_eval(value)\n            except (ValueError, SyntaxError):\n                return None\n\n        qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related']\n\n        # evaluate each query string param, and apply the queryset method with the same name\n        for param in qs_params:\n            args_or_kwargs = eval_query_value(request, param)\n            if args_or_kwargs:\n                # filter and exclude are called with a dict kwarg, the _related methods with a list\n                try:\n                    if param in ['filter', 'exclude', ]:\n                        queryset = getattr(queryset, param)(**args_or_kwargs)\n                    else:\n                        queryset = getattr(queryset, param)(*args_or_kwargs)\n\n                except FieldError as e:\n                    raise APIException(\"Error in request: {message}\".format(message=e.message))\n\n        # support for Q expressions, limited to OR-concatenated filtering\n        if request.query_params.get('q_filter1', None):\n            i = 1\n            q_queries = []\n            while request.query_params.get('q_filter{}'.format(i), None):\n                query_arg = eval_query_value(request, 'q_filter{}'.format(i))\n                if query_arg:\n                    q_queries += [query_arg]\n                i += 1\n\n            q_expr = Q(**q_queries[0])\n            for query in q_queries[1:]:\n                q_expr = q_expr | Q(**query)\n\n            queryset = queryset.filter(q_expr)\n\n        return queryset.distinct()\n", "path": "akvo/rest/filters.py"}, {"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR 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\nfrom akvo.rest.serializers.indicator_period import (\n    IndicatorPeriodFrameworkSerializer, IndicatorPeriodFrameworkLiteSerializer)\nfrom akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer\nfrom akvo.rest.serializers.rsr_serializer import BaseRSRSerializer\nfrom akvo.rsr.models import Indicator, IndicatorDimensionName, IndicatorLabel\n\nfrom rest_framework import serializers\n\n\nclass LabelListingField(serializers.RelatedField):\n\n    def to_representation(self, labels):\n        return list(labels.values_list('label_id', flat=True))\n\n    def to_internal_value(self, org_label_ids):\n        indicator = self.root.instance\n        existing_labels = set(indicator.labels.values_list('label_id', flat=True))\n        new_labels = set(org_label_ids) - existing_labels\n        deleted_labels = existing_labels - set(org_label_ids)\n        labels = [IndicatorLabel(indicator=indicator, label_id=org_label_id) for org_label_id in new_labels]\n        IndicatorLabel.objects.bulk_create(labels)\n        if deleted_labels:\n            IndicatorLabel.objects.filter(label_id__in=deleted_labels).delete()\n\n        return indicator.labels.all()\n\n\nclass IndicatorSerializer(BaseRSRSerializer):\n\n    result_unicode = serializers.ReadOnlyField(source='result.__unicode__')\n    measure_label = serializers.ReadOnlyField(source='iati_measure_unicode')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = serializers.PrimaryKeyRelatedField(\n        many=True, queryset=IndicatorDimensionName.objects.all())\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n\n    # TODO: add validation for parent_indicator\n\n\nclass IndicatorFrameworkSerializer(BaseRSRSerializer):\n\n    periods = IndicatorPeriodFrameworkSerializer(many=True, required=False, read_only=True)\n    parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n    labels = LabelListingField(queryset=IndicatorLabel.objects.all(), required=False)\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n\n\nclass IndicatorFrameworkLiteSerializer(BaseRSRSerializer):\n\n    periods = IndicatorPeriodFrameworkLiteSerializer(many=True, required=False, read_only=True)\n    parent_indicator = serializers.ReadOnlyField(source='parent_indicator_id')\n    children_aggregate_percentage = serializers.ReadOnlyField()\n    dimension_names = IndicatorDimensionNameSerializer(many=True, required=False, read_only=True)\n    labels = LabelListingField(read_only=True)\n\n    class Meta:\n        model = Indicator\n        fields = '__all__'\n", "path": "akvo/rest/serializers/indicator.py"}]}