• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Clinical-Genomics / scout / 2951

pending completion
2951

Pull #1173

travis-ci

web-flow
deactivate select for all institutes on onload if query and not admin
Pull Request #1173: Fix gene variants

24 of 24 new or added lines in 3 files covered. (100.0%)

6145 of 10727 relevant lines covered (57.29%)

0.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.46
/scout/server/blueprints/cases/controllers.py
1
# -*- coding: utf-8 -*-
2
import os
1✔
3
import itertools
1✔
4
import requests
1✔
5
import datetime
1✔
6

7
import logging
1✔
8

9
from bs4 import BeautifulSoup
1✔
10
from xlsxwriter import Workbook
1✔
11
from flask import url_for
1✔
12
from flask_mail import Message
1✔
13
import query_phenomizer
1✔
14
from flask_login import current_user
1✔
15

16
from scout.constants import (CASE_STATUSES, PHENOTYPE_GROUPS, COHORT_TAGS, SEX_MAP, PHENOTYPE_MAP, VERBS_MAP, MT_EXPORT_HEADER)
1✔
17
from scout.constants.variant_tags import MANUAL_RANK_OPTIONS, DISMISS_VARIANT_OPTIONS, GENETIC_MODELS
1✔
18
from scout.export.variant import export_mt_variants
1✔
19
from scout.server.utils import institute_and_case, user_institutes
1✔
20
from scout.parse.clinvar import clinvar_submission_header, clinvar_submission_lines
1✔
21
from scout.server.blueprints.variants.controllers import variant as variant_decorator
1✔
22
from scout.server.blueprints.variants.controllers import sv_variant
1✔
23
from scout.server.blueprints.variants.controllers import get_predictions
1✔
24
from scout.server.blueprints.genes.controllers import gene
1✔
25

26
log = logging.getLogger(__name__)
1✔
27

28
STATUS_MAP = {'solved': 'bg-success', 'archived': 'bg-warning'}
1✔
29

30

31
def cases(store, case_query, limit=100):
1✔
32
    """Preprocess case objects.
33

34
    Add the necessary information to display the 'cases' view
35

36
    Args:
37
        store(adapter.MongoAdapter)
38
        case_query(pymongo.Cursor)
39
        limit(int): Maximum number of cases to display
40

41
    Returns:
42
        data(dict): includes the cases, how many there are and the limit.
43
    """
44

45
    case_groups = {status: [] for status in CASE_STATUSES}
1✔
46
    for case_obj in case_query.limit(limit):
1✔
47
        analysis_types = set(ind['analysis_type'] for ind in case_obj['individuals'])
1✔
48

49
        case_obj['analysis_types'] = list(analysis_types)
1✔
50
        case_obj['assignees'] = [store.user(user_email) for user_email in
1✔
51
                                 case_obj.get('assignees', [])]
52
        case_groups[case_obj['status']].append(case_obj)
1✔
53
        case_obj['is_rerun'] = len(case_obj.get('analyses', [])) > 0
1✔
54
        case_obj['clinvar_variants'] = store.case_to_clinVars(case_obj['_id'])
1✔
55
    data = {
1✔
56
        'cases': [(status, case_groups[status]) for status in CASE_STATUSES],
57
        'found_cases': case_query.count(),
58
        'limit': limit,
59
    }
60
    return data
1✔
61

62

63
def case(store, institute_obj, case_obj):
1✔
64
    """Preprocess a single case.
65

66
    Prepare the case to be displayed in the case view.
67

68
    Args:
69
        store(adapter.MongoAdapter)
70
        institute_obj(models.Institute)
71
        case_obj(models.Case)
72

73
    Returns:
74
        data(dict): includes the cases, how many there are and the limit.
75

76
    """
77
    # Convert individual information to more readable format
78
    case_obj['individual_ids'] = []
×
79
    for individual in case_obj['individuals']:
×
80
        try:
×
81
            sex = int(individual.get('sex', 0))
×
82
        except ValueError as err:
×
83
            sex = 0
×
84
        individual['sex_human'] = SEX_MAP[sex]
×
85
        individual['phenotype_human'] = PHENOTYPE_MAP.get(individual['phenotype'])
×
86
        case_obj['individual_ids'].append(individual['individual_id'])
×
87

88
    case_obj['assignees'] = [store.user(user_email) for user_email in
×
89
                             case_obj.get('assignees', [])]
90

91
    # Fetch the variant objects for suspects and causatives
92
    suspects = [store.variant(variant_id) or variant_id for variant_id in
×
93
                case_obj.get('suspects', [])]
94
    causatives = [store.variant(variant_id) or variant_id for variant_id in
×
95
                  case_obj.get('causatives', [])]
96

97
    # Set of all unique genes in the default gene panels
98
    distinct_genes = set()
×
99
    case_obj['panel_names'] = []
×
100
    for panel_info in case_obj.get('panels', []):
×
101
        if not panel_info.get('is_default'):
×
102
            continue
×
103
        panel_obj = store.gene_panel(panel_info['panel_name'], version=panel_info.get('version'))
×
104
        distinct_genes.update([gene['hgnc_id'] for gene in panel_obj.get('genes', [])])
×
105
        full_name = "{} ({})".format(panel_obj['display_name'], panel_obj['version'])
×
106
        case_obj['panel_names'].append(full_name)
×
107
    case_obj['default_genes'] = list(distinct_genes)
×
108

109
    for hpo_term in itertools.chain(case_obj.get('phenotype_groups', []),
×
110
                                    case_obj.get('phenotype_terms', [])):
111
        hpo_term['hpo_link'] = ("http://compbio.charite.de/hpoweb/showterm?id={}"
×
112
                                .format(hpo_term['phenotype_id']))
113

114
    # other collaborators than the owner of the case
115
    o_collaborators = [store.institute(collab_id) for collab_id in case_obj['collaborators'] if
×
116
                       collab_id != case_obj['owner']]
117
    case_obj['o_collaborators'] = [(collab_obj['_id'], collab_obj['display_name']) for
×
118
                                   collab_obj in o_collaborators]
119

120
    irrelevant_ids = ('cust000', institute_obj['_id'])
×
121
    collab_ids = [(collab['_id'], collab['display_name']) for collab in store.institutes() if
×
122
                  (collab['_id'] not in irrelevant_ids) and
123
                  (collab['_id'] not in case_obj['collaborators'])]
124

125
    events = list(store.events(institute_obj, case=case_obj))
×
126
    for event in events:
×
127
        event['verb'] = VERBS_MAP[event['verb']]
×
128

129
    case_obj['clinvar_variants'] = store.case_to_clinVars(case_obj['display_name'])
×
130

131
    # Phenotype groups can be specific for an institute, there are some default groups
132
    pheno_groups = institute_obj.get('phenotype_groups') or PHENOTYPE_GROUPS
×
133

134
    data = {
×
135
        'status_class': STATUS_MAP.get(case_obj['status']),
136
        'other_causatives': store.check_causatives(case_obj=case_obj),
137
        'comments': store.events(institute_obj, case=case_obj, comments=True),
138
        'hpo_groups': pheno_groups,
139
        'events': events,
140
        'suspects': suspects,
141
        'causatives': causatives,
142
        'collaborators': collab_ids,
143
        'cohort_tags': COHORT_TAGS,
144
    }
145
    return data
×
146

147

148
def case_report_content(store, institute_obj, case_obj):
1✔
149
    """Gather contents to be visualized in a case report
150

151
    Args:
152
        store(adapter.MongoAdapter)
153
        institute_obj(models.Institute)
154
        case_obj(models.Case)
155

156
    Returns:
157
        data(dict)
158

159
    """
160
    variant_types = {
×
161
        'causatives_detailed': 'causatives',
162
        'suspects_detailed': 'suspects',
163
        'classified_detailed': 'acmg_classification',
164
        'tagged_detailed': 'manual_rank',
165
        'dismissed_detailed': 'dismiss_variant',
166
        'commented_detailed': 'is_commented',
167
    }
168
    data = case_obj
×
169

170
    for individual in data['individuals']:
×
171
        try:
×
172
            sex = int(individual.get('sex', 0))
×
173
        except ValueError as err:
×
174
            sex = 0
×
175
        individual['sex_human'] = SEX_MAP[sex]
×
176
        individual['phenotype_human'] = PHENOTYPE_MAP.get(individual['phenotype'])
×
177

178
    # Add the case comments
179
    data['comments'] = store.events(institute_obj, case=case_obj, comments=True)
×
180

181
    data['manual_rank_options'] = MANUAL_RANK_OPTIONS
×
182
    data['dismissed_options'] = DISMISS_VARIANT_OPTIONS
×
183
    data['genetic_models'] = dict(GENETIC_MODELS)
×
184
    data['report_created_at'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
×
185

186
    evaluated_variants = {}
×
187
    for vt in variant_types:
×
188
        evaluated_variants[vt] = []
×
189
    # We collect all causatives and suspected variants
190
    # These are handeled in separate since they are on case level
191
    for var_type in ['causatives', 'suspects']:
×
192
        #These include references to variants
193
        vt = '_'.join([var_type, 'detailed'])
×
194
        for var_id in case_obj.get(var_type,[]):
×
195
            variant_obj = store.variant(var_id)
×
196
            if not variant_obj:
×
197
                continue
×
198
            # If the variant exists we add it to the evaluated variants
199
            evaluated_variants[vt].append(variant_obj)
×
200

201
    ## get variants for this case that are either classified, commented, tagged or dismissed.
202
    for var_obj in store.evaluated_variants(case_id=case_obj['_id']):
×
203
        # Check which category it belongs to
204
        for vt in variant_types:
×
205
            keyword = variant_types[vt]
×
206
            # When found we add it to the categpry
207
            # Eac variant can belong to multiple categories
208
            if keyword in var_obj:
×
209
                evaluated_variants[vt].append(var_obj)
×
210

211
    for var_type in evaluated_variants:
×
212
        decorated_variants = []
×
213
        for var_obj in evaluated_variants[var_type]:
×
214
        # We decorate the variant with some extra information
215
            if var_obj['category'] == 'snv':
×
216
                decorated_info = variant_decorator(
×
217
                        store=store,
218
                        institute_obj=institute_obj,
219
                        case_obj=case_obj,
220
                        variant_id=None,
221
                        variant_obj=var_obj,
222
                        add_case=False,
223
                        add_other=False,
224
                        get_overlapping=False
225
                    )
226
            else:
227
                decorated_info = sv_variant(
×
228
                    store=store,
229
                    institute_id=institute_obj['_id'],
230
                    case_name=case_obj['display_name'],
231
                    variant_obj=var_obj,
232
                    add_case=False,
233
                    get_overlapping=False
234
                    )
235
            decorated_variants.append(decorated_info['variant'])
×
236
        # Add the decorated variants to the case
237
        data[var_type] = decorated_variants
×
238

239
    return data
×
240

241

242
def coverage_report_contents(store, institute_obj, case_obj, base_url):
1✔
243
    """Posts a request to chanjo-report and capture the body of the returned response to include it in case report
244

245
    Args:
246
        store(adapter.MongoAdapter)
247
        institute_obj(models.Institute)
248
        case_obj(models.Case)
249
        base_url(str): base url of server
250

251
    Returns:
252
        coverage_data(str): string rendering of the content between <body </body> tags of a coverage report
253
    """
254

255
    request_data = {}
×
256
    # extract sample ids from case_obj and add them to the post request object:
257
    request_data['sample_id'] = [ ind['individual_id'] for ind in case_obj['individuals'] ]
×
258

259
    # extract default panel names and default genes from case_obj and add them to the post request object
260
    distinct_genes = set()
×
261
    panel_names = []
×
262
    for panel_info in case_obj.get('panels', []):
×
263
        if not panel_info.get('is_default'):
×
264
            continue
×
265
        panel_obj = store.gene_panel(panel_info['panel_name'], version=panel_info.get('version'))
×
266
        full_name = "{} ({})".format(panel_obj['display_name'], panel_obj['version'])
×
267
        panel_names.append(full_name)
×
268
    panel_names = ' ,'.join(panel_names)
×
269
    request_data['panel_name'] = panel_names
×
270

271
    # add institute-specific cutoff level to the post request object
272
    request_data['level'] = institute_obj.get('coverage_cutoff', 15)
×
273

274
    #send get request to chanjo report
275
    resp = requests.get(base_url+'reports/report', params=request_data)
×
276

277
    #read response content
278
    soup = BeautifulSoup(resp.text)
×
279

280
    # remove links in the printed version of coverage report
281
    for tag in soup.find_all('a'):
×
282
        tag.replaceWith('')
×
283

284
    #extract body content using BeautifulSoup
285
    coverage_data = ''.join(['%s' % x for x in soup.body.contents])
×
286

287
    return coverage_data
×
288

289

290
def clinvar_submissions(store, user_id, institute_id):
1✔
291
    """Get all Clinvar submissions for a user and an institute"""
292
    submissions = list(store.clinvar_submissions(user_id, institute_id))
×
293
    return submissions
×
294

295

296
def clinvar_header(submission_objs, csv_type):
1✔
297
    """ Call clinvar parser to extract required fields to include in csv header from clinvar submission objects"""
298

299
    clinvar_header_obj = clinvar_submission_header(submission_objs, csv_type)
×
300
    return clinvar_header_obj
×
301

302

303
def clinvar_lines(clinvar_objects, clinvar_header):
1✔
304
    """ Call clinvar parser to extract required lines to include in csv file from clinvar submission objects and header"""
305

306
    clinvar_lines = clinvar_submission_lines(clinvar_objects, clinvar_header)
×
307
    return clinvar_lines
×
308

309

310
def mt_excel_files(store, case_obj, temp_excel_dir):
1✔
311
    """Collect MT variants and format line of a MT variant report
312
    to be exported in excel format
313

314
    Args:
315
        store(adapter.MongoAdapter)
316
        case_obj(models.Case)
317
        temp_excel_dir(os.Path): folder where the temp excel files are written to
318

319
    Returns:
320
        written_files(int): the number of files written to temp_excel_dir
321

322
    """
323
    today = datetime.datetime.now().strftime('%Y-%m-%d')
×
324
    samples = case_obj.get('individuals')
×
325

326
    query = {'chrom':'MT'}
×
327
    mt_variants = list(store.variants(case_id=case_obj['_id'], query=query, nr_of_variants= -1, sort_key='position'))
×
328

329
    written_files = 0
×
330
    for sample in samples:
×
331
        sample_id = sample['individual_id']
×
332
        sample_lines = export_mt_variants(variants=mt_variants, sample_id=sample_id)
×
333

334
        # set up document name
335
        document_name = '.'.join([case_obj['display_name'], sample_id, today]) + '.xlsx'
×
336
        workbook = Workbook(os.path.join(temp_excel_dir,document_name))
×
337
        Report_Sheet = workbook.add_worksheet()
×
338

339
        # Write the column header
340
        row = 0
×
341
        for col,field in enumerate(MT_EXPORT_HEADER):
×
342
            Report_Sheet.write(row,col,field)
×
343

344
        # Write variant lines, after header (start at line 1)
345
        for row, line in enumerate(sample_lines,1): # each line becomes a row in the document
×
346
            for col, field in enumerate(line): # each field in line becomes a cell
×
347
                Report_Sheet.write(row,col,field)
×
348
        workbook.close()
×
349

350
        if os.path.exists(os.path.join(temp_excel_dir,document_name)):
×
351
            written_files += 1
×
352

353
    return written_files
×
354

355

356
def update_synopsis(store, institute_obj, case_obj, user_obj, new_synopsis):
1✔
357
    """Update synopsis."""
358
    # create event only if synopsis was actually changed
359
    if new_synopsis and case_obj['synopsis'] != new_synopsis:
×
360
        link = url_for('cases.case', institute_id=institute_obj['_id'],
×
361
                       case_name=case_obj['display_name'])
362
        store.update_synopsis(institute_obj, case_obj, user_obj, link,
×
363
                              content=new_synopsis)
364

365

366
def hpo_diseases(username, password, hpo_ids, p_value_treshold=1):
1✔
367
    """Return the list of HGNC symbols that match annotated HPO terms.
368

369
    Args:
370
        username (str): username to use for phenomizer connection
371
        password (str): password to use for phenomizer connection
372

373
    Returns:
374
        query_result: a generator of dictionaries on the form
375
        {
376
            'p_value': float,
377
            'disease_source': str,
378
            'disease_nr': int,
379
            'gene_symbols': list(str),
380
            'description': str,
381
            'raw_line': str
382
        }
383
    """
384
    # skip querying Phenomizer unless at least one HPO terms exists
385
    try:
×
386
        results = query_phenomizer.query(username, password, *hpo_ids)
×
387
        diseases = [result for result in results
×
388
                    if result['p_value'] <= p_value_treshold]
389
        return diseases
×
390
    except SystemExit:
×
391
        return None
×
392

393

394
def rerun(store, mail, current_user, institute_id, case_name, sender, recipient):
1✔
395
    """Request a rerun by email."""
396
    institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
×
397
    user_obj = store.user(current_user.email)
×
398
    link = url_for('cases.case', institute_id=institute_id, case_name=case_name)
×
399
    store.request_rerun(institute_obj, case_obj, user_obj, link)
×
400

401
    # this should send a JSON document to the SuSy API in the future
402
    html = """
×
403
        <p>{institute}: {case} ({case_id})</p>
404
        <p>Re-run requested by: {name}</p>
405
    """.format(institute=institute_obj['display_name'],
406
               case=case_obj['display_name'], case_id=case_obj['_id'],
407
               name=user_obj['name'].encode())
408

409
    # compose and send the email message
410
    msg = Message(subject=("SCOUT: request RERUN for {}"
×
411
                           .format(case_obj['display_name'])),
412
                  html=html, sender=sender, recipients=[recipient],
413
                  # cc the sender of the email for confirmation
414
                  cc=[user_obj['email']])
415
    mail.send(msg)
×
416

417

418
def update_default_panels(store, current_user, institute_id, case_name, panel_ids):
1✔
419
    """Update default panels for a case."""
420
    institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
×
421
    user_obj = store.user(current_user.email)
×
422
    link = url_for('cases.case', institute_id=institute_id, case_name=case_name)
×
423
    panel_objs = [store.panel(panel_id) for panel_id in panel_ids]
×
424
    store.update_default_panels(institute_obj, case_obj, user_obj, link, panel_objs)
×
425

426
def vcf2cytosure(store, institute_id, case_name, individual_id):
1✔
427
    """vcf2cytosure CGH file for inidividual."""
428
    institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
×
429

430
    for individual in case_obj['individuals']:
×
431
        if individual['individual_id'] == individual_id:
×
432
            individual_obj = individual
×
433

434
    return (individual_obj['display_name'], individual_obj['vcf2cytosure'])
×
435

436
def gene_variants(store, variants_query, page=1, per_page=50):
1✔
437
    """Pre-process list of variants."""
438
    variant_count = variants_query.count()
×
439
    skip_count = per_page * max(page - 1, 0)
×
440
    more_variants = True if variant_count > (skip_count + per_page) else False
×
441
    variant_res = variants_query.skip(skip_count).limit(per_page)
×
442

443
    my_institutes = list(inst['_id'] for inst in user_institutes(store, current_user))
×
444

445
    log.debug("Institutes allowed: {}.".format(my_institutes))
×
446

447
    variants = []
×
448
    for variant_obj in variant_res:
×
449
        # hide other institutes for now
450
        if variant_obj['institute'] not in my_institutes:
×
451
            log.debug("Institute {} not allowed.".format(variant_obj['institute']))
×
452
            continue
×
453

454
        # Populate variant case_display_name
455
        variant_case_obj = store.case(case_id=variant_obj['case_id'])
×
456
        if not variant_case_obj:
×
457
            # A variant with missing case was encountered
458
            continue
×
459
        case_display_name = variant_case_obj.get('display_name')
×
460
        variant_obj['case_display_name'] = case_display_name
×
461

462
        genome_build = variant_case_obj.get('genome_build', '37')
×
463
        if genome_build not in ['37','38']:
×
464
            genome_build = '37'
×
465

466
        # Update the HGNC symbols if they are not set
467
        variant_genes = variant_obj.get('genes')
×
468
        if variant_genes is not None:
×
469
            for gene_obj in variant_genes:
×
470
                # If there is no hgnc id there is nothin we can do
471
                if not gene_obj['hgnc_id']:
×
472
                    continue
×
473
                # Else we collect the gene object and check the id
474
                if gene_obj.get('hgnc_symbol') is None or gene_obj.get('description') is None:
×
475
                    hgnc_gene = store.hgnc_gene(gene_obj['hgnc_id'], build=genome_build)
×
476
                    if not hgnc_gene:
×
477
                        continue
×
478
                    gene_obj['hgnc_symbol'] = hgnc_gene['hgnc_symbol']
×
479
                    gene_obj['description'] = hgnc_gene['description']
×
480

481
        # Populate variant HGVS and predictions
482
        gene_ids = []
×
483
        gene_symbols = []
×
484
        hgvs_c = []
×
485
        hgvs_p = []
×
486
        variant_genes = variant_obj.get('genes')
×
487

488
        if variant_genes is not None:
×
489
            functional_annotation = ''
×
490

491
            for gene_obj in variant_genes:
×
492
                hgnc_id = gene_obj['hgnc_id']
×
493
                gene_symbol = gene(store, hgnc_id)['symbol']
×
494
                gene_ids.append(hgnc_id)
×
495
                gene_symbols.append(gene_symbol)
×
496

497
                hgvs_nucleotide = '-'
×
498
                # gather HGVS info from gene transcripts
499
                transcripts_list = gene_obj.get('transcripts')
×
500
                for transcript_obj in transcripts_list:
×
501
                    if transcript_obj.get('is_canonical') and transcript_obj.get('is_canonical') is True:
×
502
                        hgvs_nucleotide = str(transcript_obj.get('coding_sequence_name'))
×
503
                        hgvs_protein = str(transcript_obj.get('protein_sequence_name'))
×
504
                hgvs_c.append(hgvs_nucleotide)
×
505
                hgvs_p.append(hgvs_protein)
×
506

507
            log.debug("HGVS: {} {} {}.".format(gene_symbols, hgvs_c, hgvs_p))
×
508

509
            if len(gene_symbols) == 1:
×
510
                if(hgvs_p[0] != "None"):
×
511
                    hgvs = hgvs_p[0]
×
512
                elif(hgvs_c[0] != "None"):
×
513
                    hgvs = hgvs_c[0]
×
514
                else:
515
                    hgvs = "-"
×
516
                variant_obj['hgvs'] = hgvs
×
517

518
            # populate variant predictions for display
519
            variant_obj.update(get_predictions(variant_genes))
×
520

521
        variants.append(variant_obj)
×
522

523
    return {
×
524
        'variants': variants,
525
        'more_variants': more_variants,
526
    }
527

528
def multiqc(store, institute_id, case_name):
1✔
529
    """Find MultiQC report for the case."""
530
    institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
×
531
    return dict(
×
532
        institute=institute_obj,
533
        case=case_obj,
534
    )
535

536

537
def get_sanger_unevaluated(store, institute_id, user_id):
1✔
538
    """Get all variants for an institute having Sanger validations ordered but still not evaluated
539

540
        Args:
541
            store(scout.adapter.MongoAdapter)
542
            institute_id(str)
543

544
        Returns:
545
            unevaluated: a list that looks like this: [ {'case1': [varID_1, varID_2, .., varID_n]}, {'case2' : [varID_1, varID_2, .., varID_n]} ],
546
                         where the keys are case_ids and the values are lists of variants with Sanger ordered but not yet validated
547

548
    """
549

550
    # Retrieve a list of ids for variants with Sanger ordered grouped by case from the 'event' collection
551
    # This way is much faster than querying over all variants in all cases of an institute
552
    sanger_ordered_by_case = store.sanger_ordered(institute_id, user_id)
1✔
553
    unevaluated = []
1✔
554

555
    # for each object where key==case and value==[variant_id with Sanger ordered]
556
    for item in sanger_ordered_by_case:
1✔
557
        case_id = item['_id']
1✔
558
        # Get the case to collect display name
559
        case_obj = store.case(case_id=case_id)
1✔
560

561
        if not case_obj: # the case might have been removed
1✔
562
            continue
×
563

564
        case_display_name = case_obj.get('display_name')
1✔
565

566
        # List of variant document ids
567
        varid_list = item['vars']
1✔
568

569
        unevaluated_by_case = {}
1✔
570
        unevaluated_by_case[case_display_name] = []
1✔
571

572
        for var_id in varid_list:
1✔
573
            # For each variant with sanger validation ordered
574
            variant_obj = store.variant(document_id=var_id, case_id=case_id)
1✔
575

576
            # Double check that Sanger was ordered (and not canceled) for the variant
577
            if variant_obj is None or variant_obj.get('sanger_ordered') is None or variant_obj.get('sanger_ordered') is False:
1✔
578
                continue
×
579

580
            validation = variant_obj.get('validation', 'not_evaluated')
1✔
581

582
            # Check that the variant is not evaluated
583
            if validation in ['True positive', 'False positive']:
1✔
584
                continue
1✔
585

586
            unevaluated_by_case[case_display_name].append(variant_obj['_id'])
1✔
587

588
        # If for a case there is at least one Sanger validation to evaluate add the object to the unevaluated objects list
589
        if len(unevaluated_by_case[case_display_name]) > 0:
1✔
590
            unevaluated.append(unevaluated_by_case)
1✔
591

592
    return unevaluated
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc