cmspsgp31/anubis

View on GitHub
anubis/views/app_view_mixin.py

Summary

Maintainability
D
2 days
Test Coverage
# Copyright © 2016, Ugo Pozo
#             2016, Câmara Municipal de São Paulo

# app_view_mixin.py - mixin for providing a view for the basic search interface.

# This file is part of Anubis.

# Anubis is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# Anubis is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""
This module contains views, mixins and helpers to define views that use
Anubis's facilities to perform searches.
"""

from rest_framework.renderers import JSONRenderer

from django.conf.urls import url
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _

from .state_view_mixin import StateViewMixin
from .caching import CachedSearchMixin

class AppViewMixin(StateViewMixin):
    record_zoom = "record_zoom"
    record_list = "record_list"
    app_theme = "app_theme"
    extra_control = None
    no_user_text = ""

    @classmethod
    def url_search(cls, app_prefix=None, **kwargs):
        if app_prefix is not None:
            app_prefix = "{}_".format(app_prefix)
        else:
            app_prefix = ""

        name = "{}html_search".format(app_prefix)

        search_url = "^{search_slug}/{m}{p}{s}{expr}$".format(**{
            "search_slug": cls.search_slug,
            "m": cls._url_part_model(),
            "p": cls._url_part_page(),
            "s": cls._url_part_sort(),
            "expr": cls._url_part_expr()
        })

        return url(search_url, cls.as_view(**kwargs), name=name)

    @classmethod
    def url_details(cls, app_prefix=None, **kwargs):
        if app_prefix is not None:
            app_prefix = "{}_".format(app_prefix)
        else:
            app_prefix = ""

        name = "{}html_details".format(app_prefix)

        details_url = "^{details_slug}/{m}{id}$".format(**{
            "details_slug": cls.details_slug,
            "m": cls._url_part_model(),
            "id": cls._url_part_details_id()
        })

        return url(details_url, cls.as_view(**kwargs), name=name)

    @classmethod
    def url_search_and_details(cls, app_prefix=None, **kwargs):
        if app_prefix is not None:
            app_prefix = "{}_".format(app_prefix)
        else:
            app_prefix = ""

        name = "{}html_search_and_details".format(app_prefix)

        s_and_d_url = ("^{details_slug}/{m}{id}/"
                       "{search_slug}/{p}{s}{expr}$").format(**{
                           "details_slug": cls.details_slug,
                           "m": cls._url_part_model(),
                           "id": cls._url_part_details_id(),
                           "search_slug": cls.search_slug,
                           "p": cls._url_part_page(),
                           "s": cls._url_part_sort(),
                           "expr": cls._url_part_expr(),
                       })

        return url(s_and_d_url, cls.as_view(**kwargs), name=name)

    def get(self, request, *args, **kwargs):
        self._prepare_attributes()

        context = self.get_context_data()

        return self.render_to_response(context)

    def get_state(self):
        base_state = dict(super().get_state())

        base_state.update({
            "tokenEditor": self.get_token_state(),
            "applicationData": self.get_application_data(),
            "models": self.get_models_meta(),
            "user": self.get_user_data(),
            "templates": self.get_templates(),
       })

        if self.user_serializer is None:
            base_state["noUserText"] = self.no_user_text

        return base_state

    def get_serializer(self, *args, **kwargs):
        kwargs["context"] = self.get_serializer_context()
        return self.get_serializer_class()(*args, **kwargs)

    def get_serializer_context(self):
        return {
            'request': self.request,
            'view': self
        }

    def get_context_data(self, **kwargs):
        if not hasattr(self, 'object_list'):
            self.object_list = []

        context = dict(super().get_context_data(**kwargs))
        full_state = self.get_full_state()

        context["anubis_state"] = JSONRenderer().render(full_state)
        context["analytics_id"] = getattr(settings, "ANALYTICS_ID", None)

        return context

    def get_models_meta(self):
        return {key: {"names": (model._meta.verbose_name.title(),
                                model._meta.verbose_name_plural.title()),
                      "order": self._model_order.index(key)
                     }
                for key, model in self._model_lookup.items()}

    def get_user_data(self):
        if self.user_serializer is None:
            return None

        return self.user_serializer(self.request.user).data \
            if self.request.user.is_authenticated() else {}

    def get_templates(self):
        from django.template.loader import get_template

        def render(template):
            return get_template("{}.js".format(template)).render()

        extra_control = None

        if self.is_multi_modeled:
            if hasattr(self.record_zoom, "items"):
                record_zoom = {model: render(template) for model, template
                               in self.record_zoom.items()}
            else:
                rendered = render(self.record_zoom)
                record_zoom = {model: rendered
                               for model in self._model_lookup.keys()}

            if hasattr(self.record_list, "items"):
                record_list = {model: render(template) for model, template
                               in self.record_list.items()}
            else:
                rendered = render(self.record_list)
                record_list = {model: rendered
                               for model in self._model_lookup.keys()}

            if self.extra_control is not None:
                if hasattr(self.extra_control, "items"):
                    extra_control = {model: render(template)
                                     for model, template
                                     in self.extra_control.items()}
                else:
                    rendered = render(self.extra_control)
                    extra_control = {model: rendered
                                     for model in self._model_lookup.keys()}

        else:
            record_zoom = {"_default": render(self.record_zoom)}
            record_list = {"_default": render(self.record_list)}

            if self.extra_control is not None:
                extra_control = {"_default": render(self.extra_control)}


        return {
            "extraControl": extra_control,
            "record": record_zoom,
            "search": record_list,
            "appTheme": render(self.app_theme)
        }

    def get_token_state(self):
        return {
            "canSearch": True,
            "shouldSearch": False,
            "fieldsets": self.get_fieldsets(),
            "counter": 1000000000,
        }

    def get_application_data(self):
        react_search_route = "{}/{}{}{}{}".format(
            self.search_slug,
            ":model/" if self.is_multi_modeled else "",
            ":page/" if self.is_paginated else "",
            ":sorting/" if self.is_sortable else "",
            "*"
        )

        react_search_html = "{}/{}/{}{}{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.search_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${page}/" if self.is_paginated else "",
            "${sorting}/" if self.is_sortable else "",
            "${expr}"
        )

        react_search_api = "{}/{}/{}/{}{}{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.api_prefix,
            self.search_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${page}/" if self.is_paginated else "",
            "${sorting}/" if self.is_sortable else "",
            "${expr}"
        )

        react_details_route = "{}/{}{}".format(
            self.details_slug,
            ":model/" if self.is_multi_modeled else "",
            ":id"
        )

        react_details_html = "{}/{}/{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.details_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${id}"
        )

        react_details_api = "{}/{}/{}/{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.api_prefix,
            self.details_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${id}"
        )

        react_search_and_details_route = "{}/{}{}/{}/{}{}{}".format(
            self.details_slug,
            ":model/" if self.is_multi_modeled else "",
            ":id",
            self.search_slug,
            ":page/" if self.is_paginated else "",
            ":sorting/" if self.is_sortable else "",
            "*"
        )

        react_search_and_details_html = "{}/{}/{}{}/{}/{}{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.details_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${id}",
            self.search_slug,
            "${page}/" if self.is_paginated else "",
            "${sorting}/" if self.is_sortable else "",
            "${expr}"
        )

        react_search_and_details_api = "{}/{}/{}/{}{}/{}/{}{}{}".format(
            self.base_url if not self.base_url == "/" else "",
            self.api_prefix,
            self.details_slug,
            "${model}/" if self.is_multi_modeled else "",
            "${id}",
            self.search_slug,
            "${page}/" if self.is_paginated else "",
            "${sorting}/" if self.is_sortable else "",
            "${expr}"
        )

        sorting_default = self.sorting_default
        default_model = self.default_model

        if not self.is_multi_modeled:
            sorting_default = {"_default": self.sorting_default}
            default_model = "_default"
        elif not self.is_sortable:
            sorting_default = dict.fromkeys(self._model_lookup.keys())

        return {
            "title": "Anubis Search Interface",
            "footer": ("© 2016-19, Câmara Municipal de São Paulo, "
                       "Secretaria de Documentação, "
                       "Equipe de Documentação do Legislativo."),
            "baseURL": self.base_url,

            "searchRoute": react_search_route,
            "searchHtml": react_search_html,
            "searchApi": react_search_api,

            "detailsRoute": react_details_route,
            "detailsHtml": react_details_html,
            "detailsApi": react_details_api,

            "searchAndDetailsRoute": react_search_and_details_route,
            "searchAndDetailsHtml": react_search_and_details_html,
            "searchAndDetailsApi": react_search_and_details_api,

            "defaultModel": default_model,
            "defaultFilter": self.get_default_filter(),

            "sortingDefaults": sorting_default,

            "sidebarLinks": {
                "list": self.sidebar_links,
                "title": self.sidebar_links_title,
                "admin": reverse('admin:index'),
                "password": reverse('admin:password_change'),
                "login": reverse('admin:login'),
                "logout": reverse('admin:logout'),
            },

        }

    def get_fieldsets(self):
        return {filter_name: {"description": filter_.description,
                              "fields": [self.render_field(filter_.fields[n]) \
                                         for n in filter_.field_keys],
                              }
                for filter_name, filter_ in self.get_filters().items()}

    def get_default_filter(self):
        if self.default_filter is not None:
            return self.default_filter

        keys = self.get_filters().keys()

        return keys[0]