Passed
Pull Request — master (#30)
by
unknown
03:59
created

validation.helpers.ValidationSummary.__init__()   A

Complexity

Conditions 1

Size

Total Lines 54
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 54
rs 9.16
c 0
b 0
f 0
cc 1
nop 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A validation.helpers.create_validation_summary_object() 0 12 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Feb 19 16:15:35 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import json
10
import logging
11
import requests
12
13
from django.db.models import Q
14
from django.core.exceptions import ObjectDoesNotExist
15
16
from image_validation import validation, ValidationResult
17
from image_validation.static_parameters import ruleset_filename as \
18
    IMAGE_RULESET
19
20
from common.constants import BIOSAMPLE_URL
21
from image_app.models import Name
22
from biosample.helpers import parse_image_alias, get_model_object
23
from validation.models import ValidationSummary
24
25
# Get an instance of a logger
26
logger = logging.getLogger(__name__)
27
28
29
# a class to deal with temporary issues from EBI servers
30
class OntologyCacheError(Exception):
31
    """Identifies temporary issues with EBI servers and
32
    image_validation.use_ontology.OntologyCache objects"""
33
34
35
# a class to deal with errors in ruleset (that are not user errors but
36
# errors within InjectTool and image_validation library)
37
class RulesetError(Exception):
38
    """Indentifies errors in ruleset"""
39
40
41
class MetaDataValidation():
42
    """A class to deal with IMAGE-ValidationTool ruleset objects"""
43
44
    ruleset = None
45
46
    def __init__(self, ruleset_filename=IMAGE_RULESET):
47
        self.read_in_ruleset(ruleset_filename)
48
49
        # check validation rules
50
        ruleset_errors = self.check_ruleset()
51
52
        if ruleset_errors != []:
53
            raise RulesetError(
54
                "Error with ruleset: %s" % "; ".join(ruleset_errors))
55
56
    def read_in_ruleset(self, ruleset_filename):
57
        try:
58
            self.ruleset = validation.read_in_ruleset(ruleset_filename)
59
60
        except json.JSONDecodeError as message:
61
            logger.error(
62
                "Error with 'https://www.ebi.ac.uk/ols/api/': %s" % (
63
                    str(message)))
64
65
            raise OntologyCacheError(
66
                "Issue with 'https://www.ebi.ac.uk/ols/api/'")
67
68
    def check_usi_structure(self, record: object) -> object:
69
        """Check data against USI rules"""
70
71
        # this function need its input as a list
72
        return validation.check_usi_structure(record)
73
74
    def check_ruleset(self):
75
        """Check ruleset"""
76
77
        return validation.check_ruleset(self.ruleset)
78
79
    def check_duplicates(self, record):
80
        """Check duplicates in data"""
81
82
        return validation.check_duplicates(record)
83
84
    def check_biosample_id_target(
85
            self, biosample_id, record_id, record_result):
86
87
        """
88
        Check if a target biosample_id exists or not. If it is present, ok.
89
        Otherwise a ValidationResultColumn with a warning
90
91
        Args:
92
            biosample_id (str): the desidered biosample id
93
            record_id (str): is the name of the object in the original data
94
                source
95
            record_result (ValidationResult.ValidationResultRecord):
96
                an image_validation result object
97
98
        Returns:
99
            ValidationResult.ValidationResultRecord: an updated
100
            image_validation object
101
        """
102
103
        url = f"{BIOSAMPLE_URL}/{biosample_id}"
104
        response = requests.get(url)
105
        status = response.status_code
106
        if status != 200:
107
            record_result.add_validation_result_column(
108
                ValidationResult.ValidationResultColumn(
109
                    "Warning",
110
                    f"Fail to retrieve record {biosample_id} from "
111
                    f"BioSamples as required in the relationship",
112
                    record_id,
113
                    'sampleRelationships'))
114
115
        return record_result
116
117
    def check_relationship(self, record, record_result):
118
        """
119
        Check relationship for an Animal/Sample record and return a list
120
        of dictionaries (to_biosample() objects) of related object
121
122
        Args:
123
            record (dict): An Animal/Sample.to_biosample() dictionary object
124
            record_result (ValidationResult.ValidationResultRecord):
125
                an image_validation result object
126
127
        Returns:
128
            list: a list of dictionaries of relate objects
129
            ValidationResult.ValidationResultRecord: an updated
130
            image_validation object
131
        """
132
133
        # get relationship from a to_biosample() dictionary object
134
        relationships = record.get('sampleRelationships', [])
135
136
        # as described in image_validation.Submission.Submission
137
        # same as record["title"], is the original name of the object id DS
138
        record_id = record['attributes']["Data source ID"][0]['value']
139
140
        # related objects (from UID goes here)
141
        related = []
142
143
        for relationship in relationships:
144
            if 'accession' in relationship:
145
                target = relationship['accession']
146
147
                # check biosample target and update record_result if necessary
148
                record_result = self.check_biosample_id_target(
149
                    target, record_id, record_result)
150
151
            # HINT: should I check aliases? they came from PK and are related
152
            # in the same submission. I can't have a sample without an animal
153
            # since animal is a foreign key of sample (which doesn't tolerate
154
            # NULL). Even mother and father are related through keys. If
155
            # missing, no information about mother and father could be
156
            # determined
157
            else:
158
                # could be a parent relationship for an animal, or the animal
159
                # where this sample comes from
160
                target = relationship['alias']
161
162
                # test for object existence in db. Use biosample.helpers
163
                # method to derive a model object from database, then get
164
                # its related data
165
                try:
166
                    material_obj = get_model_object(
167
                        *parse_image_alias(target))
168
                    related.append(material_obj.to_biosample())
169
170
                except ObjectDoesNotExist:
171
                    record_result.add_validation_result_column(
172
                        ValidationResult.ValidationResultColumn(
173
                            "Error",
174
                            f"Could not locate the referenced record {target}",
175
                            record_id, 'sampleRelationships'))
176
177
        return related, record_result
178
179
    def validate(self, record):
180
        """
181
        Check attributes for record by calling image_validation methods
182
183
        Args:
184
            record (dict): An Animal/Sample.to_biosample() dictionary object
185
186
        Returns:
187
            ValidationResult.ValidationResultRecord: an image_validation
188
            object
189
        """
190
191
        # this validated in general way
192
        result = self.ruleset.validate(record)
193
194
        # as defined in image_valdiation.Submission, I will skip further
195
        # validation check
196
        if result.get_overall_status() == "Error":
197
            logger.warning(
198
                "record: %s has errors. Skipping context validation" % (
199
                        record["title"]))
200
201
        else:
202
            # context validation evaluate relationships. Get them
203
            related, result = self.check_relationship(record, result)
204
205
            # this validate context (attributes that depends on another one)
206
            result = validation.context_validation(record, result, related)
207
208
        return result
209
210
211
def construct_validation_message(submission):
212
    """
213
    Function will return dict with all the data required to construct
214
    validation message
215
216
    Args:
217
        submission (image_app.models.Submission) : submission to get data from
218
219
    Returns:
220
        dict: dictionary with all required data for validation message
221
    """
222
    try:
223
        validation_summary_animal = ValidationSummary.objects.get(
224
            submission=submission, type='animal')
225
        validation_summary_sample = ValidationSummary.objects.get(
226
            submission=submission, type='sample')
227
        validation_message = dict()
228
229
        # Number of animal and samples
230
        validation_message[
231
            'animals'] = validation_summary_animal.get_all_count()
232
        validation_message[
233
            'samples'] = validation_summary_sample.get_all_count()
234
235
        # Number of unknow validations
236
        validation_message['animal_unkn'] = validation_summary_animal \
237
            .get_unknown_count()
238
        validation_message['sample_unkn'] = validation_summary_sample \
239
            .get_unknown_count()
240
241
        # Number of problem validations
242
        validation_message['animal_issues'] = validation_summary_animal. \
243
            get_issues_count()
244
        validation_message['sample_issues'] = validation_summary_sample. \
245
            get_issues_count()
246
        return validation_message
247
    except ObjectDoesNotExist:
248
        return None
249
250
251
def create_validation_summary_object(submission, object_type, count):
252
    """
253
    This function will add 1 to all_count property of validation summary
254
    Args:
255
        submission (image_app.models.Submission): submission object
256
        object_type (str): animal or sample
257
        count (int): now much animals or samples were in submission
258
    """
259
    validation_summary, created = ValidationSummary.objects.get_or_create(
260
        submission=submission, type=object_type)
261
    validation_summary.all_count += count
262
    validation_summary.save()
263