frontend/upload.py

Summary

Maintainability
A
45 mins
Test Coverage
A
100%
from typing import Dict

from django import forms
from django.forms.utils import flatatt
from django.utils.html import escape


DEFAULT_FILE_EXTENSIONS = (".xlsx", ".xls", ".csv")


class UploadWidget(forms.widgets.FileInput):
    '''
    This widget represents an upload widget that the user can
    easily drag-and-drop files into.

    It is tightly coupled to upload.js.
    '''

    def __init__(self, attrs=None, degraded=False, required=True,
                 accept=DEFAULT_FILE_EXTENSIONS,
                 extra_instructions='XLS, XLSX, or CSV format, please.',
                 existing_filename=None):
        super().__init__(attrs=attrs)
        self.required = required
        self.degraded = degraded
        self.accept = accept
        self.extra_instructions = extra_instructions
        self.existing_filename = existing_filename

    def render(self, name, value, attrs=None):
        final_attrs: Dict[str, str] = {}
        if attrs:
            final_attrs.update(attrs)

        final_attrs['accept'] = ",".join(self.accept)
        final_attrs['is'] = 'upload-input'

        id_for_label = final_attrs.get('id', '')
        widget_attrs = {}

        if self.degraded:
            widget_attrs['data-force-degradation'] = ''

        nojs_preamble = ''
        if self.existing_filename:
            if self.required:
                raise AssertionError(
                    'Using an existing filename is incompatible with '
                    'the "required" attribute'
                )
            widget_attrs['data-fake-initial-filename'] = self.existing_filename
            nojs_preamble = (
                'You\'ve already uploaded '
                '<code>{}</code>. '.format(
                    escape(self.existing_filename)
                ) +
                'You can keep using it or select a new file to replace it.'
            )

        label_txt = 'Choose file'
        final_attrs['aria-label'] = label_txt

        return "\n".join([
            '<upload-widget%s>' % flatatt(widget_attrs),
            '  <span class="nojs-preamble">%s</span>' % nojs_preamble,
            '  %s' % super().render(name, value, final_attrs),
            '  <div class="upload-chooser">',
            '    <label for="%s">%s</label>' % (id_for_label, label_txt),
            '    <span>%s</span>' % self.extra_instructions,
            '  </div>',
            '</upload-widget>'
        ])