Repository: liqd/a4-meinberlin

Issue:
dashboard descriptive text for Merkmale in module Ideensammlung on stage
**URL:** https://meinberlin-stage.liqd.net/dashboard/modules/ideensammlung-38/labels/
**user:** initiator
**expected behaviour:** Explanation for step "Merkmale bearbeiten" in module Ideensammlung is correct. 
**behaviour:** Refers to Kategorien: "Hier können Sie Kategorien anlegen. Dadurch können alle Beiträge einer Kategorie zugeordnet und entsprechend gefiltert werden."
**important screensize:** -
**device & browser:** -
**Comment/Question:** -

Screenshot?

![image](https://user-images.githubusercontent.com/94360651/191977136-6de3b743-d419-481c-9d35-5d669e3807a7.png)



Discussion:

Resolving patch:
diff --git a/meinberlin/apps/budgeting/api.py b/meinberlin/apps/budgeting/api.py
--- a/meinberlin/apps/budgeting/api.py
+++ b/meinberlin/apps/budgeting/api.py
@@ -11,6 +11,7 @@
 from adhocracy4.categories import get_category_icon_url
 from adhocracy4.categories import has_icons
 from adhocracy4.categories.models import Category
+from adhocracy4.labels.models import Label
 from meinberlin.apps.contrib.filters import IdeaCategoryFilterBackend
 from meinberlin.apps.contrib.filters import OrderingFilterWithDailyRandom
 from meinberlin.apps.contrib.templatetags.contrib_tags import \
@@ -52,6 +53,7 @@ def list(self, request, *args, **kwargs):
         """
         filters = {}
 
+        # category filter
         categories = Category.objects.filter(
             module=self.module
         )
@@ -73,6 +75,21 @@ def list(self, request, *args, **kwargs):
             if has_icons(self.module):
                 filters['category']['icons'] = category_icons
 
+        # label filter
+        labels = Label.objects.filter(
+            module=self.module
+        )
+        if labels:
+            label_choices = [('', _('All')), ]
+            for label in labels:
+                label_choices += (str(label.pk), label.name),
+
+            filters['labels'] = {
+                'label': _('Label'),
+                'choices': label_choices,
+            }
+
+        # archived filter
         filters['is_archived'] = {
             'label': _('Archived'),
             'choices': [
@@ -83,6 +100,7 @@ def list(self, request, *args, **kwargs):
             'default': 'false',
         }
 
+        # ordering filter
         ordering_choices = [('-created', _('Most recent')), ]
         if self.module.has_feature('rate', Proposal):
             ordering_choices += ('-positive_rating_count', _('Most popular')),
@@ -115,7 +133,7 @@ class ProposalViewSet(ModuleMixin,
                        OrderingFilterWithDailyRandom,
                        IdeaCategoryFilterBackend,
                        SearchFilter,)
-    filter_fields = ('is_archived', 'category',)
+    filter_fields = ('is_archived', 'category', 'labels',)
     ordering_fields = ('created',
                        'comment_count',
                        'positive_rating_count',
diff --git a/meinberlin/apps/budgeting/forms.py b/meinberlin/apps/budgeting/forms.py
--- a/meinberlin/apps/budgeting/forms.py
+++ b/meinberlin/apps/budgeting/forms.py
@@ -26,6 +26,17 @@ class Meta:
                                'of my proposal you can contact me.'),
             'contact_phone': _('Telephone number'),
         }
+        help_texts = {
+            'category': _('Assign your proposal to a category. This '
+                          'automatically appears in the display of your '
+                          'proposal. The list of all proposals can be '
+                          'filtered by category.'),
+            'labels': _('Specify your proposal with one or more labels. '
+                        'These will automatically appear in the display of '
+                        'your proposal. In addition, the list of all '
+                        'proposals can be filtered by labels.'),
+
+        }
 
     class Media:
         js = ('budgeting_disable_contact.js',)
diff --git a/meinberlin/apps/budgeting/serializers.py b/meinberlin/apps/budgeting/serializers.py
--- a/meinberlin/apps/budgeting/serializers.py
+++ b/meinberlin/apps/budgeting/serializers.py
@@ -2,6 +2,7 @@
 from rest_framework import serializers
 
 from adhocracy4.categories.models import Category
+from adhocracy4.labels.models import Label
 from meinberlin.apps.votes.models import TokenVote
 from meinberlin.apps.votes.models import VotingToken
 
@@ -20,6 +21,15 @@ def to_representation(self, category):
         return {'id': category.pk, 'name': category.name}
 
 
+class LabelListingField(serializers.StringRelatedField):
+
+    def to_internal_value(self, label):
+        return Label.objects.get(pk=label)
+
+    def to_representation(self, label):
+        return {'id': label.pk, 'name': label.name}
+
+
 class ProposalSerializer(serializers.ModelSerializer):
 
     creator = serializers.SerializerMethodField()
@@ -27,6 +37,7 @@ class ProposalSerializer(serializers.ModelSerializer):
     positive_rating_count = serializers.SerializerMethodField()
     negative_rating_count = serializers.SerializerMethodField()
     category = CategoryField()
+    labels = LabelListingField(many=True)
     url = serializers.SerializerMethodField()
     moderator_feedback = serializers.SerializerMethodField()
     session_token_voted = serializers.SerializerMethodField()
@@ -35,15 +46,16 @@ class ProposalSerializer(serializers.ModelSerializer):
     class Meta:
         model = Proposal
         fields = ('budget', 'category', 'comment_count', 'created', 'modified',
-                  'creator', 'is_archived', 'name', 'negative_rating_count',
-                  'positive_rating_count', 'url', 'pk', 'moderator_feedback',
-                  'point_label', 'session_token_voted', 'vote_allowed')
+                  'creator', 'is_archived', 'labels', 'name',
+                  'negative_rating_count', 'positive_rating_count', 'url',
+                  'pk', 'moderator_feedback', 'point_label',
+                  'session_token_voted', 'vote_allowed')
         read_only_fields = ('budget', 'category', 'comment_count', 'created',
-                            'modified', 'creator', 'is_archived', 'name',
-                            'negative_rating_count', 'positive_rating_count',
-                            'url', 'pk', 'moderator_feedback',
-                            'point_label', 'session_token_voted',
-                            'vote_allowed')
+                            'modified', 'creator', 'is_archived', 'labels',
+                            'name', 'negative_rating_count',
+                            'positive_rating_count', 'url', 'pk',
+                            'moderator_feedback', 'point_label',
+                            'session_token_voted', 'vote_allowed')
 
     def get_creator(self, proposal):
         return proposal.creator.username


Test patch: