project-callisto/callisto-core

View on GitHub
callisto_core/wizard_builder/view_helpers.py

Summary

Maintainability
A
2 hrs
Test Coverage
import logging
from copy import copy

from django.urls import reverse

from .data_helper import SerializedDataHelper
from .managers import FormManager

logger = logging.getLogger(__name__)


class StepsHelper(object):
    done_name = "done"
    review_name = "Review"
    next_name = "Next"
    back_name = "Back"
    wizard_goto_name = "wizard_goto_step"
    wizard_current_name = "wizard_current_step"
    wizard_form_fields = [wizard_current_name, wizard_goto_name]

    def __init__(self, view):
        self.view = view

    @property
    def step_count(self):
        return len(self.view.forms)

    @property
    def current(self):
        step = getattr(self.view, "curent_step", 0)
        if isinstance(step, str):
            return step
        elif step <= self.last:
            return step
        else:
            return self.last

    @property
    def last(self):
        return self.step_count - 1

    @property
    def next(self):
        return self.adjust_step(1)

    @property
    def next_is_done(self):
        if isinstance(self.current, int):
            return self.next == self.done_name
        else:
            return False

    @property
    def current_is_done(self):
        return self.current == self.done_name

    @property
    def current_url(self):
        return self.url(self.current)

    @property
    def first_url(self):
        return self.url(0)

    @property
    def last_url(self):
        return self.url(self.last)

    @property
    def done_url(self):
        return self.url(self.done_name)

    @property
    def _goto_step_back(self):
        return self._goto_step(self.back_name)

    @property
    def _goto_step_next(self):
        return self._goto_step(self.next_name)

    @property
    def _goto_step_review(self):
        return self._goto_step(self.review_name)

    def parse_step(self, step):
        if step == self.done_name:
            return step
        else:
            return int(step)

    def url(self, step):
        return reverse(
            self.view.request.resolver_match.view_name, kwargs={"step": step}
        )

    def overflowed(self, step):
        return int(step) > int(self.last)

    def finished(self, step):
        return self._goto_step_review or step == self.done_name

    def set_from_post(self):
        if self._goto_step_back:
            self.view.curent_step = self.adjust_step(-1)
        if self._goto_step_next:
            self.view.curent_step = self.adjust_step(1)

    def adjust_step(self, adjustment):
        step = self.view.curent_step + adjustment
        if step >= self.step_count:
            return self.done_name
        else:
            return step

    def _goto_step(self, step_type):
        post = self.view.request.POST
        return post.get(self.wizard_goto_name, None) == step_type


class StorageHelper(object):
    data_manager = SerializedDataHelper
    form_manager = FormManager

    # NOTE: overriden to 'data' by EncryptedReportStorageHelper
    storage_data_key = "wizard_form_data"

    # WARNING: do not change! record data is keyed on this value
    storage_form_key = "wizard_form_serialized"

    def __init__(self, view):
        # TODO: scope down inputs
        self.view = view
        self.site_id = view.get_site_id()
        self.session = view.request.session
        self.init_storage()

    @property
    def cleaned_form_data(self):
        storage = self.current_data_from_storage()
        return self.data_manager.get_zipped_data(
            data=storage[self.storage_data_key], forms=storage[self.storage_form_key]
        )

    @property
    def answers_for_current_step(self):
        # get the current data
        data = self.current_data_from_storage()
        # create a set of forms from form storage + post data
        new_data = copy(data)
        new_data[self.storage_data_key] = self.view.request.POST
        forms = self.get_form_models(new_data)
        # get the cleaned data from those forms, add it to answer data
        form = forms[self.view.curent_step]
        data[self.storage_data_key].update(form.cleaned_data)
        # return answer data
        return data[self.storage_data_key]

    @property
    def serialized_forms(self):
        return self.form_manager.get_serialized_forms(site_id=self.site_id)

    def get_form_models(self, data=None):
        if not data:
            data = self.current_data_from_storage()
        return self.form_manager.get_form_models(
            form_data=data[self.storage_form_key],
            answer_data=data[self.storage_data_key],
            site_id=self.site_id,
        )

    def update(self):
        """
        primary class functionality method, updates the data in storage
        """
        self.add_data_to_storage(self.answers_for_current_step)

    def current_data_from_storage(self):
        return {
            self.storage_data_key: self.session.get(self.storage_data_key, {}),
            self.storage_form_key: self.session.get(self.storage_form_key, {}),
        }

    def add_data_to_storage(self, answer_data):
        self.session[self.storage_data_key] = answer_data

    def init_storage(self):
        self.session.setdefault(self.storage_form_key, self.serialized_forms)
        self.session.setdefault(self.storage_data_key, {})


class WizardViewTemplateHelpers(object):
    # TODO: these sould all be context variables instead

    @property
    def wizard_prev_step_exists(self):
        return self.steps.current

    @property
    def wizard_next_is_done(self):
        return self.steps.next_is_done

    @property
    def wizard_current_step(self):
        return self.steps.current

    @property
    def wizard_goto_name(self):
        return self.steps.wizard_goto_name

    @property
    def wizard_current_name(self):
        return self.steps.wizard_current_name

    @property
    def wizard_review_name(self):
        return self.steps.review_name

    @property
    def wizard_next_name(self):
        return self.steps.next_name

    @property
    def wizard_back_name(self):
        return self.steps.back_name