Skip to content
Snippets Groups Projects
item.py 12.4 KiB
Newer Older
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _
from django.views.generic.edit import FormView
import transport
from ..constants import ITEM_TYPE_CATEGORY, DEFAULT_ITEM_TYPE
class ItemTypeNotFound(Exception):
    """ Exception raised internally when an item type is not found """
    pass


class ItemNotFound(Exception):
    """ Exception raised internally when an item is not found """
    pass

class AddEditItemView(FormView):
    template_name = "hid/add_edit_item.html"
    form_class = AddEditItemForm
Martin Burchell's avatar
Martin Burchell committed
    tag_fields = ('tags',)
    tag_delimiter = ','
    def _initialize_item(self, item_id, item_type):
        """ Initialize the view's item from the given item id or item_type
            self.item_type to the item type (either from the given type
                of from the given item). If the given item has no item
                type, DEFAULT_ITEM_TYPE is assumed
            self.item to the item object (or None)
            self.item_terms to a dictionary of taxonomy to list of terms
            item_id (int): Item id to initialize from
            item_type (str): Item type to initialize item_type from.
                This is used only if item_id is None.

        Raises:
            ItemNotFound: If the item was not found
            ItemTypeNotFound: If the item type was not found
        """
        self.item = None
        self.item_type = None
        self.item_terms = {}
        if item_id:
            try:
                self.item = transport.items.get(item_id)
            except transport.exceptions.TransportException:
                raise ItemNotFound()
            self.item_terms = {}
            for term in self.item['terms']:
                taxonomy = term['taxonomy']
                if taxonomy == 'item-types':
                    self.item_type = term
                if taxonomy not in self.item_terms:
                    self.item_terms[taxonomy] = []
                self.item_terms[taxonomy].append(term)
        elif item_type:
            matches = transport.terms.list(
                taxonomy='item-types',
                name=item_type
            )
            if len(matches) == 0:
                raise ItemTypeNotFound()
            else:
                self.item_type = matches[0]
        # We guarantee there is always an item type
        if self.item_type is None:
            self.item_type = DEFAULT_ITEM_TYPE

    def get(self, request, *args, **kwargs):
        """ get request handler

        If the URL defines an item_id, we load the corresponding item
        to make it available for forms.
        """
            self._initialize_item(
                kwargs.get('item_id'), kwargs.get('item_type')
            )
        except ItemNotFound:
            return self._response(
                self.request.GET.get('next', '/'),
                messages.ERROR,
                (_('Item with id %s could not be found') %
                 str(kwargs.get('item_id')))
            )
        except ItemTypeNotFound:
            return self._response(
                self.request.GET.get('next', '/'),
                messages.ERROR,
                (_('Item type %s could not be found') %
                 str(kwargs.get('item_type')))
            )
        return super(AddEditItemView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        """ post request handler

        If the URL defines an item_id, we load the corresponding item
        to make it available for forms.

Alice Heaton's avatar
Alice Heaton committed
        We handle cancel and delete here, as the form doesn't need to be
            self._initialize_item(
                kwargs.get('item_id'), kwargs.get('item_type')
            )
        except ItemNotFound:
            return self._response(
                self.request.GET.get('next', '/'),
                messages.ERROR,
                (_('Item with id %s could not be found') %
                 str(kwargs.get('item_id')))
            )
        except ItemTypeNotFound:
            return self._response(
                self.request.GET.get('next', '/'),
                messages.ERROR,
                (_('Item type %s could not be found') %
                 str(kwargs.get('item_type')))
            )
        if 'cancel' in self.request.POST['action']:
            return self._response(
                self.request.POST['next'],
                messages.INFO,
                _('No action performed')
            )
        if 'delete' in self.request.POST['action']:
            return self._delete_item()

        return super(AddEditItemView, self).post(request, *args, **kwargs)

    def get_initial(self):
        """ Return the form object's initial values for the current item """
        if self.item is None:
            initial = {
                'id': 0,
                'timestamp': datetime.now(),
                'next': self.request.GET.get('next', self.request.path)
            }
        else:
            initial = {
                'id': self.item['id'],
                'body': self.item['body'],
                'timestamp': self.item['timestamp'],
                'next': self.request.GET.get(
                    'next',
                    self.request.META.get('HTTP_REFERER', reverse('dashboard'))),

        taxonomy = ITEM_TYPE_CATEGORY.get(self.item_type['name'])
        if (taxonomy and taxonomy in self.item_terms
                and len(self.item_terms[taxonomy]) > 0):
            initial['category'] = self.item_terms[taxonomy][0]['name']
        for taxonomy, terms in self.item_terms.iteritems():
Martin Burchell's avatar
Martin Burchell committed
            if taxonomy in self.tag_fields:
                term_names = [t['name'] for t in terms]
                initial[taxonomy] = self.tag_delimiter.join(term_names)
    def get_form(self, form_class=None):
        if form_class is None:
            form_class = self.form_class

        return form_class(self.item_type['name'], **self.get_form_kwargs())
        """ Get the form's context data

        We invoke FormView's get_context_data and add the current
        item.
        """
        context = super(AddEditItemView, self).get_context_data(**kwargs)

        context['item_type_label'] = self.item_type['long_name']

        # Add the width of the option row to the context
        option_row_widget_count = 1  # We always have 'created'
        if 'category' in context['form'].fields:
        if 'region' in context['form'].fields:
            option_row_widget_count += 1
        context['option_row_width'] = 12 / option_row_widget_count

        return context

    def form_valid(self, form):
        """ Form submit handler """
        item_description = self._get_item_description()
        taxonomy = ITEM_TYPE_CATEGORY.get(self.item_type['name'])
        item_id = int(form.cleaned_data['id'])
            if item_id == 0:
                self.item = self._create_item(form, taxonomy)
                message = _("%s %d successfully created.") % (
                    item_description,
                    self.item['id']
                )
                message_code = messages.SUCCESS
            else:
                self._update_item(
                    item_id, form, taxonomy
                )
                message = _("%s %d successfully updated.") % (
                    item_description,
                    item_id,
                )
                message_code = messages.SUCCESS
        except transport.exceptions.TransportException as e:
            message = e.message.get('detail')
            if message is None:
                message = e.message

            message_code = messages.ERROR
        return self._response(
            form.cleaned_data['next'],
            message_code,
            message)
    def _separate_form_data(self, form):
        data = dict(form.cleaned_data)
        category = data.pop('category', None)
        data.pop('id', None)

Martin Burchell's avatar
Martin Burchell committed
        tags = {}
        for (field_name, field_value) in data.iteritems():
Martin Burchell's avatar
Martin Burchell committed
            if field_name in self.tag_fields:
                tags[field_name] = field_value
                regular_fields[field_name] = field_value
Martin Burchell's avatar
Martin Burchell committed
        return category, tags, regular_fields
Martin Burchell's avatar
Martin Burchell committed
    def _add_tags(self, item_id, tags):
        for (taxonomy, value) in tags.iteritems():
            transport.items.delete_all_terms(item_id, taxonomy)
            term_names = [t.strip() for t in value.split(self.tag_delimiter)]
            transport.items.add_terms(item_id, taxonomy, term_names)
    def _update_item(self, item_id, form, taxonomy):
        """ Update the given item

            Args:
                item_id (int): Item id to update
                form (Form): Valid form object containing fields
                taxonomy (str or None): Taxonomy of the item's
                    category field, if any

            Raises:
                TransportException: On API errors
        """
Martin Burchell's avatar
Martin Burchell committed
        category, tags, regular_fields = self._separate_form_data(

        transport.items.update(item_id, regular_fields)

        # TODO: Combine terms into single transaction
        if taxonomy:
            if category:
                transport.items.add_terms(item_id, taxonomy, category)
            else:
                transport.items.delete_all_terms(item_id, taxonomy)

Martin Burchell's avatar
Martin Burchell committed
        self._add_tags(item_id, tags)
    def _create_item(self, form, taxonomy):
        """ Create the given item

            Args:
                item_id (int): Item id to update
                form (Form): Valid form object containing fields
                taxonomy (str or None): Taxonomy of the item's
                    category field, if any

            Returns:
                dict: The created item

            Raises:
                TransportException: On API errors
        """
Martin Burchell's avatar
Martin Burchell committed
        category, tags, regular_fields = self._separate_form_data(
        created_item = transport.items.create(regular_fields)

        # TODO: Combine terms into single transaction
        # TODO: Don't set this here - handle more generically as hidden form
        # parameter
        transport.items.add_terms(
            created_item['id'], 'data-origins', 'Form Entry',
        )
        transport.items.add_terms(
            created_item['id'], 'item-types', self.item_type['name']
        )
        if taxonomy and category:
            transport.items.add_terms(created_item['id'], taxonomy, category)
Martin Burchell's avatar
Martin Burchell committed
        self._add_tags(created_item['id'], tags)
    def form_invalid(self, form):
        """ Form invalid handler """
        messages.add_message(
            self.request,
            messages.ERROR,
            _("The form could not be submitted."
              "Please correct the errors and submit it again.")
        )
        return super(AddEditItemView, self).form_invalid(form)

    def _response(self, url, message_type, message):
        """ Log a message and return an HTTP Response

        Args:
            url (str): URL to redirect to
            message_type (str): Message type to log (from message.INFO, etc.)
            message (str): Message to log
        Returns:
            HttpResponseRedirect: Response object
        """
        messages.add_message(self.request, message_type, message)
        return HttpResponseRedirect(url)

    def _delete_item(self):
        id = self.item['id']
        transport.items.delete(id)

        item_description = self._get_item_description()

        return self._response(
            self._get_next_url_for_delete(),
            messages.SUCCESS,
            _("%s %d successfully deleted.") % (
                item_description,
                id,
            )
        )

    def _get_next_url_for_delete(self):
        next_url = self.request.POST.get('next', reverse('dashboard'))

        return next_url

    def _get_item_description(self):