"""
Django views for interacting with Build objects
"""

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.utils.translation import ugettext as _
from django.core.exceptions import ValidationError
from django.views.generic import DetailView, ListView, UpdateView
from django.forms import HiddenInput
from django.urls import reverse

from part.models import Part
from .models import Build, BuildItem, BuildOrderAttachment
from . import forms
from stock.models import StockLocation, StockItem

from InvenTree.views import AjaxUpdateView, AjaxCreateView, AjaxDeleteView
from InvenTree.views import InvenTreeRoleMixin
from InvenTree.helpers import str2bool, extract_serial_numbers, normalize
from InvenTree.status_codes import BuildStatus


class BuildIndex(InvenTreeRoleMixin, ListView):
    """ View for displaying list of Builds
    """
    model = Build
    template_name = 'build/index.html'
    context_object_name = 'builds'
    role_required = 'build.view'

    def get_queryset(self):
        """ Return all Build objects (order by date, newest first) """
        return Build.objects.order_by('status', '-completion_date')

    def get_context_data(self, **kwargs):

        context = super(BuildIndex, self).get_context_data(**kwargs).copy()

        context['BuildStatus'] = BuildStatus

        context['active'] = self.get_queryset().filter(status__in=BuildStatus.ACTIVE_CODES)

        context['completed'] = self.get_queryset().filter(status=BuildStatus.COMPLETE)
        context['cancelled'] = self.get_queryset().filter(status=BuildStatus.CANCELLED)

        return context


class BuildCancel(AjaxUpdateView):
    """ View to cancel a Build.
    Provides a cancellation information dialog
    """

    model = Build
    ajax_template_name = 'build/cancel.html'
    ajax_form_title = _('Cancel Build')
    context_object_name = 'build'
    form_class = forms.CancelBuildForm
    role_required = 'build.change'

    def validate(self, build, form, **kwargs):

        confirm = str2bool(form.cleaned_data.get('confirm_cancel', False))

        if not confirm:
            form.add_error('confirm_cancel', _('Confirm build cancellation'))

    def save(self, build, form, **kwargs):
        """
        Cancel the build.
        """

        build.cancelBuild(self.request.user)

    def get_data(self):
        return {
            'danger': _('Build was cancelled')
        }


class BuildAutoAllocate(AjaxUpdateView):
    """ View to auto-allocate parts for a build.
    Follows a simple set of rules to automatically allocate StockItem objects.

    Ref: build.models.Build.getAutoAllocations()
    """

    model = Build
    form_class = forms.AutoAllocateForm
    context_object_name = 'build'
    ajax_form_title = _('Allocate Stock')
    ajax_template_name = 'build/auto_allocate.html'
    role_required = 'build.change'

    def get_initial(self):
        """
        Initial values for the form.
        """

        initials = super().get_initial()

        # Pointing to a particular build output?
        output = self.get_param('output')

        if output:
            try:
                output = StockItem.objects.get(pk=output)
                initials['output'] = output
            except (ValueError, StockItem.DoesNotExist):
                pass

        return initials

    def get_context_data(self, *args, **kwargs):
        """
        Get the context data for form rendering.
        """

        context = {}

        build = self.get_object()

        form = self.get_form()

        output_id = form['output'].value()

        try:
            output = StockItem.objects.get(pk=output_id)
        except (ValueError, StockItem.DoesNotExist):
            output = None

        if output:
            context['output'] = output
            context['allocations'] = build.getAutoAllocations(output)

        context['build'] = build

        return context

    def get_form(self):

        form = super().get_form()

        if form['output'].value():
            # Hide the 'output' field
            form.fields['output'].widget = HiddenInput()

        return form

    def validate(self, build, form, **kwargs):

        output = form.cleaned_data.get('output', None)

        if not output:
            form.add_error(None, _('Build output must be specified'))

    def save(self, build, form, **kwargs):
        """
        Once the form has been validated,
        perform auto-allocations
        """

        output = form.cleaned_data.get('output', None)

        build.autoAllocate(output)

    def get_data(self):
        return {
            'success': _('Allocated stock to build output'),
        }


class BuildOutputCreate(AjaxUpdateView):
    """
    Create a new build output (StockItem) for a given build.
    """

    model = Build
    form_class = forms.BuildOutputCreateForm
    ajax_template_name = 'build/build_output_create.html'
    ajax_form_title = _('Create Build Output')
    role_required = 'build.change'

    def validate(self, build, form, **kwargs):
        """
        Validation for the form:
        """

        quantity = form.cleaned_data.get('output_quantity', None)
        serials = form.cleaned_data.get('serial_numbers', None)

        # Check that the serial numbers are valid
        if serials:
            try:
                extracted = extract_serial_numbers(serials, quantity)

                if extracted:
                    # Check for conflicting serial numbers
                    conflicts = build.part.find_conflicting_serial_numbers(extracted)

                    if len(conflicts) > 0:
                        msg = ",".join([str(c) for c in conflicts])
                        form.add_error(
                            'serial_numbers',
                            _('Serial numbers already exist') + ': ' + msg,
                        )

            except ValidationError as e:
                form.add_error('serial_numbers', e.messages)

        else:
            # If no serial numbers are provided, should they be?
            if build.part.trackable:
                form.add_error('serial_numbers', _('Serial numbers required for trackable build output'))

    def save(self, build, form, **kwargs):
        """
        Create a new build output
        """

        data = form.cleaned_data

        quantity = data.get('output_quantity', None)
        batch = data.get('batch', None)

        serials = data.get('serial_numbers', None)

        if serials:
            serial_numbers = extract_serial_numbers(serials, quantity)
        else:
            serial_numbers = None

        build.create_build_output(
            quantity,
            serials=serial_numbers,
            batch=batch,
        )

    def get_initial(self):

        initials = super().get_initial()

        build = self.get_object()

        # Calculate the required quantity
        quantity = max(0, build.remaining - build.incomplete_count)
        initials['quantity'] = quantity

        return initials

    def get_form(self):

        build = self.get_object()
        part = build.part

        context = self.get_form_kwargs()

        # Pass the 'part' through to the form,
        # so we can add the next serial number as a placeholder
        context['build'] = build

        form = self.form_class(**context)

        # If the part is not trackable, hide the serial number input
        if not part.trackable:
            form.fields['serial_numbers'].widget = HiddenInput()

        return form


class BuildOutputDelete(AjaxUpdateView):
    """
    Delete a build output (StockItem) for a given build.

    Form is a simple confirmation dialog
    """

    model = Build
    form_class = forms.BuildOutputDeleteForm
    ajax_form_title = _('Delete Build Output')
    role_required = 'build.delete'

    def get_initial(self):

        initials = super().get_initial()

        output = self.get_param('output')

        initials['output_id'] = output

        return initials

    def validate(self, build, form, **kwargs):

        data = form.cleaned_data

        confirm = data.get('confirm', False)

        if not confirm:
            form.add_error('confirm', _('Confirm unallocation of build stock'))
            form.add_error(None, _('Check the confirmation box'))

        output_id = data.get('output_id', None)
        output = None

        try:
            output = StockItem.objects.get(pk=output_id)
        except (ValueError, StockItem.DoesNotExist):
            pass

        if output:
            if not output.build == build:
                form.add_error(None, _('Build output does not match build'))
        else:
            form.add_error(None, _('Build output must be specified'))

    def save(self, build, form, **kwargs):

        output_id = form.cleaned_data.get('output_id')

        output = StockItem.objects.get(pk=output_id)

        build.deleteBuildOutput(output)

    def get_data(self):
        return {
            'danger': _('Build output deleted'),
        }


class BuildUnallocate(AjaxUpdateView):
    """ View to un-allocate all parts from a build.

    Provides a simple confirmation dialog with a BooleanField checkbox.
    """

    model = Build
    form_class = forms.UnallocateBuildForm
    ajax_form_title = _("Unallocate Stock")
    ajax_template_name = "build/unallocate.html"
    role_required = 'build.change'

    def get_initial(self):

        initials = super().get_initial()

        # Pointing to a particular build output?
        output = self.get_param('output')

        if output:
            initials['output_id'] = output

        # Pointing to a particular part?
        part = self.get_param('part')

        if part:
            initials['part_id'] = part

        return initials

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

        build = self.get_object()
        form = self.get_form()
        
        confirm = request.POST.get('confirm', False)

        output_id = request.POST.get('output_id', None)

        try:
            output = StockItem.objects.get(pk=output_id)
        except (ValueError, StockItem.DoesNotExist):
            output = None

        part_id = request.POST.get('part_id', None)

        try:
            part = Part.objects.get(pk=part_id)
        except (ValueError, Part.DoesNotExist):
            part = None

        valid = False

        if confirm is False:
            form.add_error('confirm', _('Confirm unallocation of build stock'))
            form.add_error(None, _('Check the confirmation box'))
        else:
            build.unallocateStock(output=output, part=part)
            valid = True

        data = {
            'form_valid': valid,
        }

        return self.renderJsonResponse(request, form, data)


class BuildComplete(AjaxUpdateView):
    """
    View to mark the build as complete.

    Requirements:
    - There can be no outstanding build outputs
    - The "completed" value must meet or exceed the "quantity" value
    """

    model = Build
    form_class = forms.CompleteBuildForm
    role_required = 'build.change'
    ajax_form_title = _('Complete Build Order')
    ajax_template_name = 'build/complete.html'

    def validate(self, build, form, **kwargs):

        if not build.can_complete:
            form.add_error(None, _('Build order cannot be completed'))

    def save(self, build, form, **kwargs):
        """
        Perform the build completion step
        """

        build.complete_build(self.request.user)

    def get_data(self):
        return {
            'success': _('Completed build order')
        }


class BuildOutputComplete(AjaxUpdateView):
    """
    View to mark a particular build output as Complete.

    - Notifies the user of which parts will be removed from stock.
    - Removes allocated items from stock
    - Deletes pending BuildItem objects
    """

    model = Build
    form_class = forms.CompleteBuildOutputForm
    context_object_name = "build"
    ajax_form_title = _("Complete Build Output")
    ajax_template_name = "build/complete_output.html"
    role_required = 'build.change'

    def get_form(self):
        
        build = self.get_object()

        form = super().get_form()

        # Extract the build output object
        output = None
        output_id = form['output'].value()

        try:
            output = StockItem.objects.get(pk=output_id)
        except (ValueError, StockItem.DoesNotExist):
            pass

        if output:
            if build.isFullyAllocated(output):
                form.fields['confirm_incomplete'].widget = HiddenInput()

        return form

    def validate(self, build, form, **kwargs):

        data = form.cleaned_data

        output = data.get('output', None)

        if output:

            quantity = data.get('quantity', None)

            if quantity and quantity > output.quantity:
                form.add_error('quantity', _('Quantity to complete cannot exceed build output quantity'))

            if not build.isFullyAllocated(output):
                confirm = str2bool(data.get('confirm_incomplete', False))

                if not confirm:
                    form.add_error('confirm_incomplete', _('Confirm completion of incomplete build'))

        else:
            form.add_error(None, _('Build output must be specified'))

    def get_initial(self):
        """ Get initial form data for the CompleteBuild form

        - If the part being built has a default location, pre-select that location
        """
        
        initials = super().get_initial()
        build = self.get_object()

        if build.part.default_location is not None:
            try:
                location = StockLocation.objects.get(pk=build.part.default_location.id)
                initials['location'] = location
            except StockLocation.DoesNotExist:
                pass

        output = self.get_param('output', None)

        if output:
            try:
                output = StockItem.objects.get(pk=output)
            except (ValueError, StockItem.DoesNotExist):
                output = None

        # Output has not been supplied? Try to "guess"
        if not output:

            incomplete = build.get_build_outputs(complete=False)

            if incomplete.count() == 1:
                output = incomplete[0]

        if output is not None:
            initials['output'] = output

        initials['location'] = build.destination

        return initials

    def get_context_data(self, **kwargs):
        """
        Get context data for passing to the rendered form

        - Build information is required
        """

        build = self.get_object()

        context = {}

        # Build object
        context['build'] = build

        form = self.get_form()

        output = form['output'].value()

        if output:
            try:
                output = StockItem.objects.get(pk=output)
                context['output'] = output
                context['fully_allocated'] = build.isFullyAllocated(output)
                context['allocated_parts'] = build.allocatedParts(output)
                context['unallocated_parts'] = build.unallocatedParts(output)
            except (ValueError, StockItem.DoesNotExist):
                pass

        return context

    def save(self, build, form, **kwargs):

        data = form.cleaned_data

        location = data.get('location', None)
        output = data.get('output', None)

        # Complete the build output
        build.completeBuildOutput(
            output,
            self.request.user,
            location=location,
        )
    
    def get_data(self):
        """ Provide feedback data back to the form """
        return {
            'success': _('Build output completed')
        }


class BuildNotes(UpdateView):
    """ View for editing the 'notes' field of a Build object.
    """

    context_object_name = 'build'
    template_name = 'build/notes.html'
    model = Build
    role_required = 'build.view'

    fields = ['notes']

    def get_success_url(self):
        return reverse('build-notes', kwargs={'pk': self.get_object().id})

    def get_context_data(self, **kwargs):

        ctx = super().get_context_data(**kwargs)
        
        ctx['editing'] = str2bool(self.request.GET.get('edit', ''))

        return ctx


class BuildDetail(DetailView):
    """ Detail view of a single Build object. """

    model = Build
    template_name = 'build/detail.html'
    context_object_name = 'build'
    role_required = 'build.view'

    def get_context_data(self, **kwargs):

        ctx = super(DetailView, self).get_context_data(**kwargs)

        build = self.get_object()

        ctx['bom_price'] = build.part.get_price_info(build.quantity, buy=False)
        ctx['BuildStatus'] = BuildStatus

        return ctx


class BuildAllocate(DetailView):
    """ View for allocating parts to a Build """
    model = Build
    context_object_name = 'build'
    template_name = 'build/allocate.html'
    role_required = ['build.change']

    def get_context_data(self, **kwargs):
        """ Provide extra context information for the Build allocation page """

        context = super(DetailView, self).get_context_data(**kwargs)

        build = self.get_object()
        part = build.part
        bom_items = part.bom_items

        context['part'] = part
        context['bom_items'] = bom_items
        context['BuildStatus'] = BuildStatus

        context['bom_price'] = build.part.get_price_info(build.quantity, buy=False)

        if str2bool(self.request.GET.get('edit', None)):
            context['editing'] = True

        return context


class BuildCreate(AjaxCreateView):
    """ View to create a new Build object """
    model = Build
    context_object_name = 'build'
    form_class = forms.EditBuildForm
    ajax_form_title = _('New Build Order')
    ajax_template_name = 'modal_form.html'
    role_required = 'build.add'

    def get_form(self):
        form = super().get_form()

        if form['part'].value():
            form.fields['part'].widget = HiddenInput()

        return form

    def get_initial(self):
        """ Get initial parameters for Build creation.

        If 'part' is specified in the GET query, initialize the Build with the specified Part
        """

        initials = super(BuildCreate, self).get_initial().copy()

        part = self.request.GET.get('part', None)

        if part:

            try:
                part = Part.objects.get(pk=part)
                # User has provided a Part ID
                initials['part'] = part
                initials['destination'] = part.get_default_location()
            except (ValueError, Part.DoesNotExist):
                pass

        initials['reference'] = Build.getNextBuildNumber()

        initials['parent'] = self.request.GET.get('parent', None)

        # User has provided a SalesOrder ID
        initials['sales_order'] = self.request.GET.get('sales_order', None)

        initials['quantity'] = self.request.GET.get('quantity', 1)

        return initials

    def get_data(self):
        return {
            'success': _('Created new build'),
        }

    def validate(self, build, form, **kwargs):
        """
        Perform extra form validation.

        - If part is trackable, check that either batch or serial numbers are calculated

        By this point form.is_valid() has been executed
        """

        pass


class BuildUpdate(AjaxUpdateView):
    """ View for editing a Build object """
    
    model = Build
    form_class = forms.EditBuildForm
    context_object_name = 'build'
    ajax_form_title = _('Edit Build Details')
    ajax_template_name = 'modal_form.html'
    role_required = 'build.change'

    def get_form(self):

        form = super().get_form()

        build = self.get_object()

        # Fields which are included in the form, but hidden
        hidden = [
            'parent',
            'sales_order',
        ]

        if build.is_complete:
            # Fields which cannot be edited once the build has been completed

            hidden += [
                'part',
                'quantity',
                'batch',
                'take_from',
                'destination',
            ]

        for field in hidden:
            form.fields[field].widget = HiddenInput()

        return form

    def get_data(self):
        return {
            'info': _('Edited build'),
        }


class BuildDelete(AjaxDeleteView):
    """ View to delete a build """

    model = Build
    ajax_template_name = 'build/delete_build.html'
    ajax_form_title = _('Delete Build')
    role_required = 'build.delete'


class BuildItemDelete(AjaxDeleteView):
    """ View to 'unallocate' a BuildItem.
    Really we are deleting the BuildItem object from the database.
    """

    model = BuildItem
    ajax_template_name = 'build/delete_build_item.html'
    ajax_form_title = _('Unallocate Stock')
    context_object_name = 'item'
    role_required = 'build.delete'

    def get_data(self):
        return {
            'danger': _('Removed parts from build allocation')
        }


class BuildItemCreate(AjaxCreateView):
    """
    View for allocating a StockItem to a build output.
    """

    model = BuildItem
    form_class = forms.EditBuildItemForm
    ajax_template_name = 'build/create_build_item.html'
    ajax_form_title = _('Allocate stock to build output')
    role_required = 'build.add'

    # The output StockItem against which the allocation is being made
    output = None

    # The "part" which is being allocated to the output
    part = None
    
    available_stock = None

    def get_context_data(self):
        """
        Provide context data to the template which renders the form.
        """

        ctx = super().get_context_data()

        if self.part:
            ctx['part'] = self.part

        if self.output:
            ctx['output'] = self.output

        if self.available_stock:
            ctx['stock'] = self.available_stock
        else:
            ctx['no_stock'] = True

        return ctx

    def validate(self, build_item, form, **kwargs):
        """
        Extra validation steps as required
        """

        data = form.cleaned_data

        stock_item = data.get('stock_item', None)
        quantity = data.get('quantity', None)

        if stock_item:
            # Stock item must actually be in stock!
            if not stock_item.in_stock:
                form.add_error('stock_item', _('Item must be currently in stock'))

            # Check that there are enough items available
            if quantity is not None:
                available = stock_item.unallocated_quantity()
                if quantity > available:
                    form.add_error('stock_item', _('Stock item is over-allocated'))
                    form.add_error('quantity', _('Available') + ': ' + str(normalize(available)))
        else:
            form.add_error('stock_item', _('Stock item must be selected'))

    def get_form(self):
        """ Create Form for making / editing new Part object """

        form = super(AjaxCreateView, self).get_form()

        self.build = None
        self.part = None
        self.output = None

        # If the Build object is specified, hide the input field.
        # We do not want the users to be able to move a BuildItem to a different build
        build_id = form['build'].value()

        if build_id is not None:
            """
            If the build has been provided, hide the widget to change the build selection.
            Additionally, update the allowable selections for other fields.
            """
            form.fields['build'].widget = HiddenInput()
            form.fields['install_into'].queryset = StockItem.objects.filter(build=build_id, is_building=True)
            self.build = Build.objects.get(pk=build_id)
        else:
            """
            Build has *not* been selected
            """
            pass

        # If the sub_part is supplied, limit to matching stock items
        part_id = form['part_id'].value()

        if part_id:
            try:
                self.part = Part.objects.get(pk=part_id)
       
            except (ValueError, Part.DoesNotExist):
                pass

        # If the output stock item is specified, hide the input field
        output_id = form['install_into'].value()

        if output_id is not None:

            try:
                self.output = StockItem.objects.get(pk=output_id)
                form.fields['install_into'].widget = HiddenInput()
            except (ValueError, StockItem.DoesNotExist):
                pass

        else:
            # If the output is not specified, but we know that the part is non-trackable, hide the install_into field
            if self.part and not self.part.trackable:
                form.fields['install_into'].widget = HiddenInput()

        if self.build and self.part:
            available_items = self.build.availableStockItems(self.part, self.output)
            form.fields['stock_item'].queryset = available_items

        self.available_stock = form.fields['stock_item'].queryset.all()

        # If there is only a single stockitem available, select it!
        if len(self.available_stock) == 1:
            form.fields['stock_item'].initial = self.available_stock[0].pk

        return form

    def get_initial(self):
        """ Provide initial data for BomItem. Look for the folllowing in the GET data:

        - build: pk of the Build object
        - part: pk of the Part object which we are assigning
        - output: pk of the StockItem object into which the allocated stock will be installed
        """

        initials = super(AjaxCreateView, self).get_initial().copy()

        build_id = self.get_param('build')
        part_id = self.get_param('part')
        output_id = self.get_param('install_into')

        # Reference to a Part object
        part = None

        # Reference to a StockItem object
        item = None
        
        # Reference to a Build object
        build = None

        # Reference to a StockItem object
        output = None

        if part_id:
            try:
                part = Part.objects.get(pk=part_id)
                initials['part_id'] = part.pk
            except Part.DoesNotExist:
                pass

        if build_id:
            try:
                build = Build.objects.get(pk=build_id)
                initials['build'] = build
            except Build.DoesNotExist:
                pass

        # If the output has been specified
        if output_id:
            try:
                output = StockItem.objects.get(pk=output_id)
                initials['install_into'] = output
            except (ValueError, StockItem.DoesNotExist):
                pass

        # Work out how much stock is required
        if build and part:
            required_quantity = build.unallocatedQuantity(part, output)
        else:
            required_quantity = None

        quantity = self.request.GET.get('quantity', None)

        if quantity is not None:
            quantity = float(quantity)
        elif required_quantity is not None:
            quantity = required_quantity
                
        item_id = self.get_param('item')

        # If the request specifies a particular StockItem
        if item_id:
            try:
                item = StockItem.objects.get(pk=item_id)
            except (ValueError, StockItem.DoesNotExist):
                pass

        # If a StockItem is not selected, try to auto-select one
        if item is None and part is not None:
            items = StockItem.objects.filter(part=part)
            if items.count() == 1:
                item = items.first()

        # Finally, if a StockItem is selected, ensure the quantity is not too much
        if item is not None:
            if quantity is None:
                quantity = item.unallocated_quantity()
            else:
                quantity = min(quantity, item.unallocated_quantity())

        if quantity is not None:
            initials['quantity'] = quantity

        return initials


class BuildItemEdit(AjaxUpdateView):
    """ View to edit a BuildItem object """

    model = BuildItem
    ajax_template_name = 'build/edit_build_item.html'
    form_class = forms.EditBuildItemForm
    ajax_form_title = _('Edit Stock Allocation')
    role_required = 'build.change'

    def get_data(self):
        return {
            'info': _('Updated Build Item'),
        }

    def get_form(self):
        """
        Create form for editing a BuildItem.

        - Limit the StockItem options to items that match the part
        """

        form = super(BuildItemEdit, self).get_form()

        # Hide fields which we do not wish the user to edit
        for field in ['build', 'stock_item']:
            if form[field].value():
                form.fields[field].widget = HiddenInput()

        form.fields['install_into'].widget = HiddenInput()

        return form


class BuildAttachmentCreate(AjaxCreateView):
    """
    View for creating a BuildAttachment
    """

    model = BuildOrderAttachment
    form_class = forms.EditBuildAttachmentForm
    ajax_form_title = _('Add Build Order Attachment')
    role_required = 'build.add'

    def save(self, form, **kwargs):
        """
        Add information on the user that uploaded the attachment
        """

        attachment = form.save(commit=False)
        attachment.user = self.request.user
        attachment.save()

    def get_data(self):
        return {
            'success': _('Added attachment')
        }

    def get_initial(self):
        """
        Get initial data for creating an attachment
        """

        initials = super().get_initial()

        try:
            initials['build'] = Build.objects.get(pk=self.request.GET.get('build', -1))
        except (ValueError, Build.DoesNotExist):
            pass

        return initials

    def get_form(self):
        """
        Hide the 'build' field if specified
        """

        form = super().get_form()

        form.fields['build'].widget = HiddenInput()
    
        return form


class BuildAttachmentEdit(AjaxUpdateView):
    """
    View for editing a BuildAttachment object
    """

    model = BuildOrderAttachment
    form_class = forms.EditBuildAttachmentForm
    ajax_form_title = _('Edit Attachment')
    role_required = 'build.change'

    def get_form(self):
        form = super().get_form()
        form.fields['build'].widget = HiddenInput()

        return form

    def get_data(self):
        return {
            'success': _('Attachment updated')
        }


class BuildAttachmentDelete(AjaxDeleteView):
    """
    View for deleting a BuildAttachment
    """

    model = BuildOrderAttachment
    ajax_form_title = _('Delete Attachment')
    context_object_name = 'attachment'
    role_required = 'build.delete'

    def get_data(self):
        return {
            'danger': _('Deleted attachment')
        }
