Repository: liqd/a4-meinberlin

Issue:
distinguish betweeen bplan, external project, container and project



Discussion:

Resolving patch:
diff --git a/meinberlin/apps/plans/models.py b/meinberlin/apps/plans/models.py
--- a/meinberlin/apps/plans/models.py
+++ b/meinberlin/apps/plans/models.py
@@ -96,6 +96,10 @@ class Meta:
     def reference_number(self):
         return '{:d}-{:05d}'.format(self.created.year, self.pk)
 
+    @property
+    def administrative_district(self):
+        return self.district
+
     @cached_property
     def published_projects(self):
         return self.projects.filter(
diff --git a/meinberlin/apps/plans/serializers.py b/meinberlin/apps/plans/serializers.py
new file mode 100644
--- /dev/null
+++ b/meinberlin/apps/plans/serializers.py
@@ -0,0 +1,205 @@
+from django.utils.translation import ugettext as _
+from easy_thumbnails.files import get_thumbnailer
+from rest_framework import serializers
+
+from adhocracy4.projects.models import Project
+from meinberlin.apps.projects import get_project_type
+
+from .models import Plan
+
+
+class CommonFields:
+
+    def get_district(self, instance):
+        city_wide = _('City wide')
+        district_name = str(city_wide)
+        if instance.administrative_district:
+            district_name = instance.administrative_district.name
+        return district_name
+
+    def get_point(self, instance):
+        point = instance.point
+        if not point:
+            point = ''
+        return point
+
+
+class ProjectSerializer(serializers.ModelSerializer, CommonFields):
+    type = serializers.SerializerMethodField()
+    title = serializers.SerializerMethodField()
+    url = serializers.SerializerMethodField()
+    organisation = serializers.SerializerMethodField()
+    point = serializers.SerializerMethodField()
+    point_label = serializers.SerializerMethodField()
+    cost = serializers.SerializerMethodField()
+    district = serializers.SerializerMethodField()
+    status = serializers.SerializerMethodField()
+    status_display = serializers.SerializerMethodField()
+    participation = serializers.SerializerMethodField()
+    participation_active = serializers.SerializerMethodField()
+    participation_string = serializers.SerializerMethodField()
+    participation_display = serializers.SerializerMethodField()
+    future_phase = serializers.SerializerMethodField()
+    active_phase = serializers.SerializerMethodField()
+    past_phase = serializers.SerializerMethodField()
+    tile_image = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Project
+        fields = ['type', 'title', 'url',
+                  'organisation', 'tile_image',
+                  'tile_image_copyright',
+                  'point', 'point_label', 'cost',
+                  'district', 'topic',
+                  'status', 'status_display',
+                  'participation_string',
+                  'participation_active',
+                  'participation', 'participation_display', 'description',
+                  'future_phase', 'active_phase',
+                  'past_phase']
+
+    def _get_participation_status_project(self, instance):
+        if instance.phases.active_phases():
+            return _('running'), True
+        elif instance.phases.future_phases():
+            try:
+                return (_('starts at {}').format
+                        (instance.phases.future_phases().first().
+                         start_date.date()),
+                        True)
+            except AttributeError:
+                return (_('starts in the future'),
+                        True)
+        else:
+            return _('done'), False
+
+    def get_type(self, instance):
+        return 'project'
+
+    def get_title(self, instance):
+        return instance.name
+
+    def get_url(self, instance):
+        if get_project_type(instance) in ('external', 'bplan'):
+            return instance.externalproject.url
+        return instance.get_absolute_url()
+
+    def get_organisation(self, instance):
+        return instance.organisation.name
+
+    def get_tile_image(self, instance):
+        image_url = ''
+        if instance.tile_image:
+            image = get_thumbnailer(instance.tile_image)['project_tile']
+            image_url = image.url
+        return image_url
+
+    def get_point_label(self, instance):
+        return ''
+
+    def get_cost(self, instance):
+        return ''
+
+    def get_status(self, instance):
+        if instance.phases.active_phases() or instance.phases.future_phases():
+            return 2
+        return 3
+
+    def get_status_display(self, instance):
+        if instance.phases.active_phases() or instance.phases.future_phases():
+            return _('Implementation')
+        return _('Done')
+
+    def get_participation(self, instance):
+        return 1
+
+    def get_participation_display(self, instance):
+        return _('Yes')
+
+    def get_future_phase(self, instance):
+        if (instance.future_phases and
+                instance.future_phases.first().start_date):
+            return str(
+                instance.future_phases.first().start_date.date())
+        return False
+
+    def get_active_phase(self, instance):
+        if instance.active_phase:
+            progress = instance.active_phase_progress
+            time_left = instance.time_left
+            return [progress, time_left]
+        return False
+
+    def get_past_phase(self, instance):
+        if instance.phases.past_phases():
+            return True
+        return False
+
+    def get_participation_string(self, instance):
+        participation_string, active = \
+            self._get_participation_status_project(instance)
+        return str(participation_string)
+
+    def get_participation_active(self, instance):
+        participation_string, active = \
+            self._get_participation_status_project(instance)
+        return active
+
+
+class PlanSerializer(serializers.ModelSerializer, CommonFields):
+    type = serializers.SerializerMethodField()
+    url = serializers.SerializerMethodField()
+    district = serializers.SerializerMethodField()
+    point = serializers.SerializerMethodField()
+    status_display = serializers.SerializerMethodField()
+    participation_active = serializers.SerializerMethodField()
+    participation_string = serializers.SerializerMethodField()
+    published_projects_count = serializers.SerializerMethodField()
+
+    class Meta:
+        model = Plan
+        fields = ['type', 'title', 'url',
+                  'organisation', 'point',
+                  'point_label', 'cost',
+                  'district', 'topic', 'status',
+                  'status_display',
+                  'participation',
+                  'participation_string',
+                  'participation_active',
+                  'published_projects_count']
+
+    def _get_participation_status_plan(self, item):
+        projects = item.projects.all() \
+            .filter(is_draft=False,
+                    is_archived=False,
+                    is_public=True)
+        if not projects:
+            return item.get_participation_display(), False
+        else:
+            status_string = self._get_status_string(projects)
+            if status_string:
+                return status_string, True
+            else:
+                return item.get_participation_display(), False
+
+    def get_type(self, instance):
+        return 'plan'
+
+    def get_url(self, instance):
+        return instance.get_absolute_url()
+
+    def get_status_display(self, instance):
+        return instance.get_status_display()
+
+    def get_published_projects_count(self, instance):
+        return instance.published_projects.count()
+
+    def get_participation_string(self, instance):
+        participation_string, active = \
+            self._get_participation_status_plan(instance)
+        return str(participation_string)
+
+    def get_participation_active(self, instance):
+        participation_string, active = \
+            self._get_participation_status_plan(instance)
+        return active
diff --git a/meinberlin/apps/plans/views.py b/meinberlin/apps/plans/views.py
--- a/meinberlin/apps/plans/views.py
+++ b/meinberlin/apps/plans/views.py
@@ -6,10 +6,11 @@
 from django.core.exceptions import ObjectDoesNotExist
 from django.urls import reverse
 from django.utils import timezone
+from django.utils.functional import cached_property
 from django.utils.translation import ugettext
 from django.utils.translation import ugettext_lazy as _
 from django.views import generic
-from easy_thumbnails.files import get_thumbnailer
+from rest_framework.renderers import JSONRenderer
 
 from adhocracy4.dashboard import mixins as a4dashboard_mixins
 from adhocracy4.exports import mixins as export_mixins
@@ -23,6 +24,7 @@
 from meinberlin.apps.projects.models import Project
 
 from . import models
+from . import serializers
 
 
 class PlanDetailView(rules_mixins.PermissionRequiredMixin,
@@ -43,202 +45,68 @@ class PlanListView(rules_mixins.PermissionRequiredMixin,
     template_name = 'meinberlin_plans/plan_list.html'
     permission_required = 'meinberlin_plans.list_plan'
 
-    def get_districts(self):
+    def get_queryset(self):
+        plans = super().get_queryset()
+        return sorted(plans,
+                      key=lambda x: x.modified or x.created,
+                      reverse=True)
+
+    @cached_property
+    def districts(self):
         try:
             return MapPreset.objects.filter(
                 category__name='Bezirke - Berlin')
         except ObjectDoesNotExist:
             return []
 
-    def _get_status_string(self, projects):
-
-        future_phase = None
-        for project in projects:
-            phases = project.phases
-            if phases.active_phases():
-                return ugettext('running')
-            if phases.future_phases() and \
-               phases.future_phases().first().start_date:
-                date = phases.future_phases().first().start_date
-                if not future_phase:
-                    future_phase = date
-                else:
-                    if date < future_phase:
-                        future_phase = date
-
-        if future_phase:
-            return ugettext('starts at {}').format(future_phase.date())
-
-    def _get_participation_status(self, item):
-        projects = item.projects.all()\
-            .filter(is_draft=False,
-                    is_archived=False,
-                    is_public=True)
-        if not projects:
-            return item.get_participation_display(), False
-        else:
-            status_string = self._get_status_string(projects)
-            if status_string:
-                return status_string, True
-            else:
-                return item.get_participation_display(), False
-
-    def _get_participation_status_project(self, project):
-        if project.phases.active_phases():
-            return ugettext('running'), True
-        elif project.phases.future_phases():
-            try:
-                return (ugettext('starts at {}').format
-                        (project.phases.future_phases().first().
-                         start_date.date()),
-                        True)
-            except AttributeError:
-                return (ugettext('starts in the future'),
-                        True)
-        else:
-            return ugettext('done'), False
-
-    def _get_status_project(self, project):
-        if project.phases.active_phases() or project.phases.future_phases():
-            return 2, ugettext('Implementation')
-        else:
-            return 3, ugettext('Done')
-
-    def _get_phase_status(self, project):
-        if (project.future_phases and
-                project.future_phases.first().start_date):
-            date_str = str(
-                project.future_phases.first().start_date.date())
-            return (date_str,
-                    False,
-                    False)
-        elif project.active_phase:
-            progress = project.active_phase_progress
-            time_left = project.time_left
-            return (False,
-                    [progress, time_left],
-                    False)
-        elif project.phases.past_phases():
-            return (False,
-                    False,
-                    True)
-        return (False,
-                False,
-                False)
-
-    def get_context_data(self, **kwargs):
-
+    @cached_property
+    def projects(self):
+        projects = Project.objects.all() \
+            .filter(is_draft=False, is_archived=False) \
+            .order_by('created')
+        not_allowed_projects = [project.id for project in projects if
+                                not self.request.user.has_perm(
+                                    'a4projects.view_project',
+                                    project)]
+        return projects.exclude(id__in=not_allowed_projects)
+
+    def get_district_polygons(self):
+        districts = self.districts
+        return json.dumps([district.polygon
+                           for district in districts])
+
+    def get_district_names(self):
         city_wide = _('City wide')
-
-        context = super().get_context_data(**kwargs)
-
-        districts = self.get_districts()
-
-        district_list = json.dumps([district.polygon
-                                    for district in districts])
+        districts = self.districts
         district_names_list = [district.name
                                for district in districts]
         district_names_list.append(str(city_wide))
-        district_names = json.dumps(district_names_list)
-        context['districts'] = district_list
-        context['district_names'] = district_names
+        return json.dumps(district_names_list)
 
+    def get_topics(self):
         topics = getattr(settings, 'A4_PROJECT_TOPICS', None)
         if topics:
-            topics = dict((x, str(y)) for x, y in topics)
+            topic_dict = dict((x, str(y)) for x, y in topics)
+            return json.dumps(topic_dict)
         else:
             raise ImproperlyConfigured('set A4_PROJECT_TOPICS in settings')
-        context['topic_choices'] = json.dumps(topics)
-
-        items = sorted(context['object_list'],
-                       key=lambda x: x.modified or x.created,
-                       reverse=True)
-
-        result = []
-
-        for item in items:
-            participation_string, active = self._get_participation_status(item)
-            district_name = str(city_wide)
-            if item.district:
-                district_name = item.district.name
-
-            result.append({
-                'type': 'plan',
-                'title': item.title,
-                'url': item.get_absolute_url(),
-                'organisation': item.organisation.name,
-                'point': item.point,
-                'point_label': item.point_label,
-                'cost': item.cost,
-                'district': district_name,
-                'topic': item.topic,
-                'status': item.status,
-                'status_display': item.get_status_display(),
-                'participation_string': participation_string,
-                'participation_active': active,
-                'participation': item.participation,
-                'participation_display': item.get_participation_display(),
-                'published_projects_count': item.published_projects.count(),
-            })
-
-        projects = Project.objects.all()\
-            .filter(is_draft=False, is_archived=False)\
-            .order_by('created')
 
-        for item in projects:
-            if self.request.user.has_perm('a4projects.view_project', item):
-                district_name = str(city_wide)
-                if item.administrative_district:
-                    district_name = item.administrative_district.name
-                point = item.point
-                if not point:
-                    point = ''
-                point_label = ''
-                cost = ''
-                participation_string, active = \
-                    self._get_participation_status_project(item)
-                status, status_display = \
-                    self._get_status_project(item)
-                participation = 1
-                participation_display = _('Yes')
-                future_phase, active_phase, past_phase = \
-                    self._get_phase_status(item)
-                image_url = ''
-                if item.tile_image:
-                    image = get_thumbnailer(item.tile_image)['project_tile']
-                    image_url = image.url
-
-                result.append({
-                    'type': 'project',
-                    'title': item.name,
-                    'url': item.get_absolute_url(),
-                    'organisation': item.organisation.name,
-                    'tile_image': image_url,
-                    'tile_image_copyright': item.tile_image_copyright,
-                    'point': point,
-                    'point_label': point_label,
-                    'cost': cost,
-                    'district': district_name,
-                    'topic': item.topic,
-                    'status': status,
-                    'status_display': str(status_display),
-                    'participation_string': str(participation_string),
-                    'participation_active': active,
-                    'participation': participation,
-                    'participation_display': str(participation_display),
-                    'description': item.description,
-                    'future_phase': future_phase,
-                    'active_phase': active_phase,
-                    'past_phase': past_phase
-                })
-
-        context['items'] = json.dumps(result)
+    def get_context_data(self, **kwargs):
+        plans_serializer = serializers.PlanSerializer(self.get_queryset(),
+                                                      many=True)
+        projects_serializer = serializers.ProjectSerializer(self.projects,
+                                                            many=True)
+        items = plans_serializer.data + projects_serializer.data
+        context = super().get_context_data(**kwargs)
+        context['districts'] = self.get_district_polygons()
+        context['district_names'] = self.get_district_names()
+        context['topic_choices'] = self.get_topics()
+        context['items'] = JSONRenderer().render(items)
         context['baseurl'] = settings.A4_MAP_BASEURL
         context['attribution'] = settings.A4_MAP_ATTRIBUTION
         context['bounds'] = json.dumps(settings.A4_MAP_BOUNDING_BOX)
         context['district'] = self.request.GET.get('district', -1)
         context['topic'] = self.request.GET.get('topic', -1)
-
         return context
 
 


Test patch: