Completed
Pull Request — master (#47)
by Paolo
19:50
created

submissions.forms   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 18
eloc 82
dl 0
loc 150
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
B SubmissionFormMixin.clean() 0 18 8
A SubmissionFormMixin.check_crbanim_columns() 0 19 2
A SubmissionFormMixin.check_file_encoding() 0 16 3
A SubmissionFormMixin.check_template_file() 0 33 5
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Jul 24 15:51:05 2018
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import magic
10
import tempfile
11
12
from django import forms
13
14
from common.forms import RequestFormMixin
15
from common.constants import CRB_ANIM_TYPE, TEMPLATE_TYPE
16
from image_app.models import Submission
17
from crbanim.helpers import CRBAnimReader
18
from excel.helpers import ExcelTemplateReader
19
20
21
class SubmissionFormMixin():
22
    def clean(self):
23
        # I can call this method without providing a 'uploaded file'
24
        # (for instance, when omitting uploaded file)
25
        if "uploaded_file" in self.cleaned_data:
26
            # avoid file type for excel types (is not an text file)
27
            if ("datasource_type" in self.cleaned_data and
28
                    self.cleaned_data["datasource_type"] != TEMPLATE_TYPE):
29
                self.check_file_encoding()
30
31
            # check crbanim files only if provided
32
            if ("datasource_type" in self.cleaned_data and
33
                    self.cleaned_data["datasource_type"] == CRB_ANIM_TYPE):
34
                self.check_crbanim_columns()
35
36
            # check template files only if provided
37
            if ("datasource_type" in self.cleaned_data and
38
                    self.cleaned_data["datasource_type"] == TEMPLATE_TYPE):
39
                self.check_template_file()
40
41
    def check_file_encoding(self):
42
        uploaded_file = self.cleaned_data['uploaded_file']
43
44
        # read one chunk of such file
45
        chunk = next(uploaded_file.chunks())
46
        magic_line = magic.from_buffer(chunk)
47
        file_type = magic_line.split(",")[0]
48
49
        if "UTF-8" not in file_type and "ASCII" not in file_type:
50
            # create message and add error
51
            msg = (
52
                "Error: file not in UTF-8 nor ASCII format: "
53
                "format was %s" % file_type)
54
55
            # raising an exception:
56
            raise forms.ValidationError(msg, code='invalid')
57
58
    def check_crbanim_columns(self):
59
        """Check if a CRBanim file has mandatory columns"""
60
61
        uploaded_file = self.cleaned_data['uploaded_file']
62
63
        # read one chunk of such file
64
        chunk = next(uploaded_file.chunks())
65
66
        # now determine if CRBanim file is valid. chunk is in binary format
67
        # neet to convert to a string, fortunately I've already check that
68
        # file is in UTF-8
69
        check, not_found = CRBAnimReader.is_valid(chunk.decode("utf-8"))
70
71
        if check is False:
72
            msg = "Error: file lacks of CRBanim mandatory columns: %s" % (
73
                not_found)
74
75
            # raising an exception:
76
            raise forms.ValidationError(msg, code='invalid')
77
78
    def check_template_file(self):
79
        """Check if template file has columns and sheets"""
80
81
        uploaded_file = self.cleaned_data['uploaded_file']
82
83
        # xlrd can manage onli files. Write a temporary file
84
        with tempfile.NamedTemporaryFile(delete=True) as tmpfile:
85
            for chunk in uploaded_file.chunks():
86
                tmpfile.write(chunk)
87
88
            # open the file with proper model
89
            reader = ExcelTemplateReader()
90
            reader.read_file(tmpfile.name)
91
92
            # check that template has at least breed, animal, sample sheets
93
            check, not_found = reader.check_sheets()
94
95
            if check is False:
96
                msg = "Error: file lacks of Template mandatory sheets: %s" % (
97
                    not_found)
98
99
                # raising an exception:
100
                raise forms.ValidationError(msg, code='invalid')
101
102
            # check that template has at least breed, animal, sample sheets
103
            check, not_found = reader.check_columns()
104
105
            if check is False:
106
                msg = "Error: file lacks of Template mandatory columns: %s" % (
107
                    not_found)
108
109
                # raising an exception:
110
                raise forms.ValidationError(msg, code='invalid')
111
112
113
class SubmissionForm(SubmissionFormMixin, RequestFormMixin, forms.ModelForm):
114
    class Meta:
115
        model = Submission
116
        fields = (
117
            'title',
118
            'description',
119
            'gene_bank_name',
120
            'gene_bank_country',
121
            'organization',
122
            'datasource_type',
123
            'datasource_version',
124
            'uploaded_file'
125
        )
126
127
        help_texts = {
128
            'uploaded_file': 'Need to be in UTF-8 or ASCII format',
129
        }
130
131
132
# I use forms.Form since I need to pass primary key as a field,
133
# and I can't use it with a modelform
134
class ReloadForm(SubmissionFormMixin, RequestFormMixin, forms.ModelForm):
135
    # custom attributes
136
    agree_reload = forms.BooleanField(
137
        label="That's fine. Replace my submission data with this file",
138
        help_text="You have to check this box to reload your data")
139
140
    class Meta:
141
        model = Submission
142
        fields = (
143
            'datasource_type',
144
            'datasource_version',
145
            'uploaded_file',
146
        )
147
148
        help_texts = {
149
            'uploaded_file': 'Need to be in UTF-8 or ASCII format',
150
        }
151