Repository: Clinical-Genomics/scout

Issue:
CSV - length -1 for a deletion of 80 genes
https://scout.scilifelab.se/cust003/17159/sv/variants?variant_type=clinical&gene_panels=EP&hgnc_symbols=&size=&chrom=&thousand_genomes_frequency=

please have a look. Something doesn't fit here.
A deletion of 1 bp can not contain 80 genes.

thanks,
Michela

Discussion:

Patch:
diff --git a/scout/build/variant.py b/scout/build/variant.py
index 3cf04f188..391398ca7 100644
--- a/scout/build/variant.py
+++ b/scout/build/variant.py
@@ -258,10 +258,20 @@ def build_variant(variant, institute_id, gene_to_panels = None,
 
     # Add the callers
     call_info = variant.get('callers', {})
+    if call_info.get('gatk'):
+        variant_obj['gatk'] = call_info['gatk']
     
-    for caller in call_info:
-        if call_info[caller]:
-            variant_obj[caller] = call_info[caller]
+    if call_info.get('samtools'):
+        variant_obj['samtools'] = call_info['samtools']
+    
+    if call_info.get('freebayes'):
+        variant_obj['freebayes'] = call_info['freebayes']
+
+    if call_info.get('mutect'):
+        variant_obj['mutect'] = call_info['mutect']
+
+    if call_info.get('pindel'):
+        variant_obj['pindel'] = call_info['pindel']
 
     # Add the conservation
     conservation_info = variant.get('conservation', {})
@@ -308,16 +318,6 @@ def build_variant(variant, institute_id, gene_to_panels = None,
     if variant.get('local_obs_hom_old'):
         variant_obj['local_obs_hom_old'] = variant['local_obs_hom_old']
 
-    # Add the sv counts:
-    if frequencies.get('clingen_cgh_benign'):
-        variant_obj['clingen_cgh_benign'] = frequencies['clingen_cgh_benign']
-    if frequencies.get('clingen_cgh_pathogenic'):
-        variant_obj['clingen_cgh_pathogenic'] = frequencies['clingen_cgh_pathogenic']
-    if frequencies.get('clingen_ngi'):
-        variant_obj['clingen_ngi'] = frequencies['clingen_ngi']
-    if frequencies.get('decipher'):
-        variant_obj['decipher'] = frequencies['decipher']
-    
     # Add the severity predictors
 
     if variant.get('cadd_score'):
diff --git a/scout/constants/__init__.py b/scout/constants/__init__.py
index 80dde1844..e6835b108 100644
--- a/scout/constants/__init__.py
+++ b/scout/constants/__init__.py
@@ -37,34 +37,3 @@ PAR_COORDINATES = {
     },
 }
 
-CALLERS = {
-    'snv': [{
-        'id': 'gatk',
-        'name': 'GATK',
-    }, {
-        'id': 'freebayes',
-        'name': 'Freebayes',
-    }, {
-        'id': 'samtools',
-        'name': 'SAMtools',
-    }, {
-        'id': 'mutect',
-        'name': 'MuTect',
-    }, {
-        'id': 'pindel',
-        'name': 'Pindel',
-    }],
-    'sv': [{
-        'id': 'cnvnator',
-        'name': 'CNVnator',
-    }, {
-        'id': 'delly',
-        'name': 'Delly',
-    }, {
-        'id': 'tiddit',
-        'name': 'TIDDIT',
-    }, {
-        'id': 'manta',
-        'name': 'Manta',
-    }]
-}
diff --git a/scout/parse/variant/callers.py b/scout/parse/variant/callers.py
index 687cccd6a..617d2624c 100644
--- a/scout/parse/variant/callers.py
+++ b/scout/parse/variant/callers.py
@@ -1,34 +1,41 @@
-from scout.constants import CALLERS
-
 
 def parse_callers(variant):
     """Parse how the different variant callers have performed
-
+    
         Args:
             variant(dict): A variant dictionary
-
+    
         Returns:
-            callers(dict): A dictionary on the form
+            callers(dict): A dictionary on the form 
             {'gatk': <filter>,'freebayes': <filter>,'samtools': <filter>}
     """
-    relevant_callers = CALLERS['sv' if variant.var_type == 'sv' else 'snv']
-    callers = {caller['id']: None for caller in relevant_callers}
+    callers = {
+        'gatk': None,
+        'freebayes': None,
+        'samtools': None,
+        'mutect': None,
+        'pindel': None,
+    }
     raw_info = variant.INFO.get('set')
     if raw_info:
         info = raw_info.split('-')
         for call in info:
             if call == 'FilteredInAll':
-                for caller in callers:
-                    callers[caller] = 'Filtered'
+                callers['gatk'] = 'Filtered'
+                callers['samtools'] = 'Filtered'
+                callers['freebayes'] = 'Filtered'
             elif call == 'Intersection':
-                for caller in callers:
-                    callers[caller] = 'Pass'
+                callers['gatk'] = 'Pass'
+                callers['samtools'] = 'Pass'
+                callers['freebayes'] = 'Pass'
             elif 'filterIn' in call:
-                for caller in callers:
-                    if caller in call:
-                        callers[caller] = 'Filtered'
-
-            elif call in set(callers.keys()):
+                if 'gatk' in call:
+                    callers['gatk'] = 'Filtered'
+                if 'samtools' in call:
+                    callers['samtools'] = 'Filtered'
+                if 'freebayes' in call:
+                    callers['freebayes'] = 'Filtered'
+            elif call in ['gatk', 'samtools', 'freebayes']:
                 callers[call] = 'Pass'
     # The following is parsing of a custom made merge
     other_info = variant.INFO.get('FOUND_IN')
diff --git a/scout/parse/variant/coordinates.py b/scout/parse/variant/coordinates.py
index fa24acf8c..5031d6b0a 100644
--- a/scout/parse/variant/coordinates.py
+++ b/scout/parse/variant/coordinates.py
@@ -19,7 +19,20 @@ def get_cytoband_coordinates(chrom, pos):
     return coordinate
 
 def get_sub_category(alt_len, ref_len, category, svtype=None):
-    """Get the subcategory"""
+    """Get the subcategory for a VCF variant
+    
+    The sub categories are:
+        'snv', 'indel', 'del', 'ins', 'dup', 'bnd', 'inv'
+    
+    Args:
+        alt_len(int)
+        ref_len(int)
+        category(str)
+        svtype(str)
+    
+    Returns:
+        subcategory(str)
+    """
     subcategory = ''
     
     if category in ('snv', 'indel', 'cancer'):
@@ -32,99 +45,147 @@ def get_sub_category(alt_len, ref_len, category, svtype=None):
     
     return subcategory
 
-def get_length(alt_len, ref_len, category, svtype=None, svlen=None):
-    """docstring for get_length"""
+def get_length(alt_len, ref_len, category, pos, end, svtype=None, svlen=None):
+    """Return the length of a variant
+    
+    Args:
+        alt_len(int)
+        ref_len(int)
+        category(str)
+        svtype(str)
+        svlen(int)
+    """
+    # -1 would indicate uncertain length
+    length = -1
     if category in ('snv', 'indel', 'cancer'):
         if ref_len == alt_len:
             length = alt_len
         else:
-            length = abs(ref_len-alt_len)
+            length = abs(ref_len - alt_len)
+    
     elif category == 'sv':
         if svtype == 'bnd':
             length = int(10e10)
         else:
             if svlen:
                 length = abs(int(svlen))
-            else:
-                # -1 would indicate uncertain length
-                length = -1
+            # Some software does not give a length but they give END
+            elif end:
+                if end != pos:
+                    length = end - pos
     return length
 
-def get_end(pos, length, alt, category, svtype=None):
-    """docstring for get_length"""
-    end = None
+def get_end(pos, alt, category, snvend=None, svend=None, svlen=None):
+    """Return the end coordinate for a variant
+    
+    Args:
+        pos(int)
+        alt(str)
+        category(str)
+        snvend(str)
+        svend(int)
+        svlen(int)
+    
+    Returns:
+        end(int)
+    """
+    # If nothing is known we set end to be same as start
+    end = pos
+    # If variant is snv or indel we know that cyvcf2 can handle end pos
     if category in ('snv', 'indel', 'cancer'):
-        end = pos + length
+        end = snvend
 
+    # With SVs we have to be a bit more careful
     elif category == 'sv':
+        # The END field from INFO usually works fine
+        end = svend
+        
+        # For some cases like insertions the callers set end to same as pos
+        # In those cases we can hope that there is a svlen...
+        if svend == pos:
+            if svlen:
+                end = pos + svlen
+        # If variant is 'BND' they have ':' in alt field
+        # Information about other end is in the alt field
         if ':' in alt:
             other_coordinates = alt.strip('ACGTN[]').split(':')
             # For BND end will represent the end position of the other end
             try:
                 end = int(other_coordinates[1])
             except ValueError as err:
-                end = pos + length
-        else:
-            end = pos + length
-    
-    return end
+                pass
 
+    return end
 
-def parse_coordinates(chrom, ref, alt, position, category, svtype, svlen, end, mate_id=None):
+def parse_coordinates(variant, category):
     """Find out the coordinates for a variant
     
     Args:
-        chrom(str)
-        ref(str)
-        alt(str)
-        position(int)
-        category(str)
-        svtype(str)
-        svlen(int)
-        end(int)
-        mate_id(str)
+        variant(cyvcf2.Variant)
     
     Returns:
         coordinates(dict): A dictionary on the form:
         {
+            'position':<int>, 
             'end':<int>, 
+            'end_chrom':<str>,
             'length':<int>, 
             'sub_category':<str>,
             'mate_id':<str>,
+            'cytoband_start':<str>,
+            'cytoband_end':<str>,
         }
     """
-    coordinates = {
-        'end': end,
-        'length': None,
-        'sub_category': None,
-        'mate_id':None,
-        'cytoband_start':None,
-        'cytoband_end':None,
-        'end_chrom':None,
-    }
+    ref = variant.REF
+    alt = variant.ALT[0]
+    chrom = variant.CHROM
+    if (chrom.startswith('chr') or chrom.startswith('CHR')):
+        chrom = chrom[3:]
+    
+    svtype = variant.INFO.get('SVTYPE')
     if svtype:
         svtype = svtype.lower()
 
+    mate_id = variant.INFO.get('MATEID')
+
+    svlen = variant.INFO.get('SVLEN')
+    
+    svend = variant.INFO.get('END')
+    snvend = int(variant.end)
+    
+    position = int(variant.POS)
+
     ref_len = len(ref)
     alt_len = len(alt)
-    coordinates['mate_id'] = mate_id
-    coordinates['sub_category'] = get_sub_category(alt_len, ref_len, category, svtype)
-    coordinates['length'] = get_length(alt_len, ref_len, category, svtype, svlen)
-    coordinates['end'] = get_end(position, coordinates['length'], alt, category, svtype)
-    coordinates['end_chrom'] = chrom
-
-    if coordinates['sub_category'] == 'bnd':    
-            if ':' in alt:
-                other_coordinates = alt.strip('ACGTN[]').split(':')
-                # BND will often be translocations between different chromosomes
-                other_chrom = other_coordinates[0]
-                coordinates['end_chrom'] = other_coordinates[0].lstrip('chrCHR')
-    
-    coordinates['cytoband_start'] = get_cytoband_coordinates(
-        chrom, position
-    )
-    coordinates['cytoband_end'] = get_cytoband_coordinates(
-        coordinates['end_chrom'], coordinates['end']
-    )
+    
+    sub_category = get_sub_category(alt_len, ref_len, category, svtype)
+    end = get_end(position, alt, category, snvend, svend)
+
+    length = get_length(alt_len, ref_len, category, position, end, svtype, svlen)
+    end_chrom = chrom
+
+    if sub_category == 'bnd':    
+        if ':' in alt:
+            other_coordinates = alt.strip('ACGTN[]').split(':')
+            # BND will often be translocations between different chromosomes
+            other_chrom = other_coordinates[0]
+            if (other_chrom.startswith('chr') or other_chrom.startswith('CHR')):
+                other_chrom = other_chrom[3:]
+            end_chrom = other_chrom
+    
+    cytoband_start = get_cytoband_coordinates(chrom, position)
+    cytoband_end = get_cytoband_coordinates(end_chrom, end)
+
+    coordinates = {
+        'position': position,
+        'end': end,
+        'length': length,
+        'sub_category': sub_category,
+        'mate_id': mate_id,
+        'cytoband_start': cytoband_start,
+        'cytoband_end': cytoband_end,
+        'end_chrom': end_chrom,
+    }
+
 
     return coordinates
diff --git a/scout/parse/variant/frequency.py b/scout/parse/variant/frequency.py
index bb2f2ea99..aef802199 100644
--- a/scout/parse/variant/frequency.py
+++ b/scout/parse/variant/frequency.py
@@ -85,39 +85,3 @@ def parse_frequency(variant, info_key):
     raw_annotation = None if raw_annotation == '.' else raw_annotation
     frequency = float(raw_annotation) if raw_annotation else None
     return frequency
-
-def parse_sv_frequencies(variant):
-    """Parsing of some custom sv frequencies
-    
-    These are very specific at the moment, this will hopefully get better over time when the 
-    field of structural variants is more developed.
-    
-    Args:
-        variant(cyvcf2.Variant)
-    
-    Returns:
-        sv_frequencies(dict)
-    """
-    frequency_keys = [
-        'clingen_cgh_benignAF', 
-        'clingen_cgh_benign',
-        'clingen_cgh_pathogenicAF',
-        'clingen_cgh_pathogenic',
-        'clingen_ngi',
-        'clingen_ngiAF',
-        'decipherAF',
-        'decipher'
-    ]
-    sv_frequencies = {}
-    
-    for key in frequency_keys:
-        value = variant.INFO.get(key, 0)
-        if 'AF' in key:
-            value = float(value)
-        else:
-            value = int(value)
-        if value > 0:
-            sv_frequencies[key] = value
-    
-    return sv_frequencies
-    
\ No newline at end of file
diff --git a/scout/parse/variant/variant.py b/scout/parse/variant/variant.py
index 625c8901d..2decdfaa7 100644
--- a/scout/parse/variant/variant.py
+++ b/scout/parse/variant/variant.py
@@ -7,7 +7,7 @@ from .genotype import parse_genotypes
 from .compound import parse_compounds
 from .clnsig import parse_clnsig
 from .gene import parse_genes
-from .frequency import (parse_frequencies, parse_sv_frequencies)
+from .frequency import parse_frequencies
 from .conservation import parse_conservations
 from .ids import parse_ids
 from .callers import parse_callers
@@ -81,20 +81,19 @@ def parse_variant(variant, case, variant_type='clinical',
             category = 'snv'
 
     parsed_variant['category'] = category
-    #sub category is 'snv', 'indel', 'del', 'ins', 'dup', 'inv', 'cnv'
-    # 'snv' and 'indel' are subcatogories of snv
-    parsed_variant['sub_category'] = None
 
     ################# General information #################
 
     parsed_variant['reference'] = variant.REF
-    # We allways assume splitted and normalized vcfs
+    
+    ### We allways assume splitted and normalized vcfs!!!
     if len(variant.ALT) > 1:
         raise VcfError("Variants are only allowed to have one alternative")
     parsed_variant['alternative'] = variant.ALT[0]
 
     # cyvcf2 will set QUAL to None if '.' in vcf
     parsed_variant['quality'] = variant.QUAL
+    
     if variant.FILTER:
         parsed_variant['filters'] = variant.FILTER.split(';')
     else:
@@ -109,38 +108,19 @@ def parse_variant(variant, case, variant_type='clinical',
 
     ################# Position specific #################
     parsed_variant['chromosome'] = chrom
-    # position = start
-    parsed_variant['position'] = int(variant.POS)
 
-    svtype = variant.INFO.get('SVTYPE')
-
-    svlen = variant.INFO.get('SVLEN')
-
-    end = int(variant.end)
-
-    mate_id = variant.INFO.get('MATEID')
-
-    coordinates = parse_coordinates(
-        chrom=parsed_variant['chromosome'],
-        ref=parsed_variant['reference'],
-        alt=parsed_variant['alternative'],
-        position=parsed_variant['position'],
-        category=parsed_variant['category'],
-        svtype=svtype,
-        svlen=svlen,
-        end=end,
-        mate_id=mate_id,
-    )
+    coordinates = parse_coordinates(variant, category)
 
+    parsed_variant['position'] = coordinates['position']
     parsed_variant['sub_category'] = coordinates['sub_category']
     parsed_variant['mate_id'] = coordinates['mate_id']
-    parsed_variant['end'] = int(coordinates['end'])
-    parsed_variant['length'] = int(coordinates['length'])
+    parsed_variant['end'] = coordinates['end']
+    parsed_variant['length'] = coordinates['length']
     parsed_variant['end_chrom'] = coordinates['end_chrom']
     parsed_variant['cytoband_start'] = coordinates['cytoband_start']
     parsed_variant['cytoband_end'] = coordinates['cytoband_end']
 
-    ################# Add rank score #################
+    ################# Add the rank score #################
     # The rank score is central for displaying variants in scout.
 
     rank_score = parse_rank_score(variant.INFO.get('RankScore', ''), genmod_key)
@@ -153,14 +133,14 @@ def parse_variant(variant, case, variant_type='clinical',
     else:
         parsed_variant['samples'] = []
 
-    ################# Add compound information #################
+    ################# Add the compound information #################
     compounds = parse_compounds(compound_info=variant.INFO.get('Compounds'),
                                 case_id=genmod_key,
                                 variant_type=variant_type)
     if compounds:
         parsed_variant['compounds'] = compounds
 
-    ################# Add inheritance patterns #################
+    ################# Add the inheritance patterns #################
 
     genetic_models = parse_genetic_models(variant.INFO.get('GeneticModels'), genmod_key)
     if genetic_models:
@@ -176,7 +156,7 @@ def parse_variant(variant, case, variant_type='clinical',
     if azqual:
         parsed_variant['azqual'] = float(azqual)
 
-    ################# Add gene and transcript information #################
+    ################# Add the gene and transcript information #################
     raw_transcripts = []
     if vep_header:
         vep_info = variant.INFO.get('CSQ')
@@ -212,7 +192,7 @@ def parse_variant(variant, case, variant_type='clinical',
 
     parsed_variant['hgnc_ids'] = list(hgnc_ids)
 
-    ################# Add clinsig prediction #################
+    ################# Add the clinsig prediction #################
     clnsig_predictions = parse_clnsig(
         acc=variant.INFO.get('CLNACC'),
         sig=variant.INFO.get('CLNSIG'),
@@ -237,7 +217,7 @@ def parse_variant(variant, case, variant_type='clinical',
     if local_obs_hom_old:
         parsed_variant['local_obs_hom_old'] = int(local_obs_hom_old)
 
-    ###################### Add severity predictions ######################
+    ###################### Add the severity predictions ######################
     cadd = parse_cadd(variant, parsed_transcripts)
     if cadd:
         parsed_variant['cadd_score'] = cadd
@@ -246,7 +226,7 @@ def parse_variant(variant, case, variant_type='clinical',
     if spidex:
         parsed_variant['spidex'] = float(spidex)
 
-    ###################### Add conservation ######################
+    ###################### Add the conservation ######################
 
     parsed_variant['conservation'] = parse_conservations(variant)
 
@@ -257,9 +237,4 @@ def parse_variant(variant, case, variant_type='clinical',
         results = [int(i) for i in rank_result.split('|')]
         parsed_variant['rank_result'] = dict(zip(rank_results_header, results))
 
-    ###################### Add SV specific annotations ######################
-    sv_frequencies = parse_sv_frequencies(variant)
-    for key in sv_frequencies:
-        parsed_variant['frequencies'][key] = sv_frequencies[key]
-
     return parsed_variant
diff --git a/scout/server/blueprints/variants/controllers.py b/scout/server/blueprints/variants/controllers.py
index 763c52e25..9175f830a 100644
--- a/scout/server/blueprints/variants/controllers.py
+++ b/scout/server/blueprints/variants/controllers.py
@@ -6,7 +6,7 @@ from flask import url_for, flash
 from flask_mail import Message
 
 from scout.constants import (CLINSIG_MAP, ACMG_MAP, MANUAL_RANK_OPTIONS, ACMG_OPTIONS,
-                             ACMG_COMPLETE_MAP, CALLERS)
+                             ACMG_COMPLETE_MAP)
 from scout.constants.acmg import ACMG_CRITERIA
 from scout.models.event import VERBS_MAP
 from scout.server.utils import institute_and_case
@@ -57,13 +57,8 @@ def sv_variant(store, institute_id, case_name, variant_id):
         ('1000G', variant_obj.get('thousand_genomes_frequency')),
         ('1000G (left)', variant_obj.get('thousand_genomes_frequency_left')),
         ('1000G (right)', variant_obj.get('thousand_genomes_frequency_right')),
-        ('ClinGen CGH (benign)', variant_obj.get('clingen_cgh_benign')),
-        ('ClinGen CGH (pathogenic)', variant_obj.get('clingen_cgh_pathogenic')),
-        ('ClinGen NGI', variant_obj.get('clingen_ngi')),
-        ('Decipher', variant_obj.get('decipher')),
     ]
 
-    variant_obj['callers'] = callers(variant_obj, category='sv')
     overlapping_snvs = (parse_variant(store, institute_obj, case_obj, variant) for variant in
                         store.overlapping(variant_obj))
 
@@ -211,7 +206,7 @@ def variant(store, institute_obj, case_obj, variant_id):
     variant_obj['alamut_link'] = alamut_link(variant_obj)
     variant_obj['spidex_human'] = spidex_human(variant_obj)
     variant_obj['expected_inheritance'] = expected_inheritance(variant_obj)
-    variant_obj['callers'] = callers(variant_obj, category='snv')
+    variant_obj['callers'] = callers(variant_obj)
 
     for gene_obj in variant_obj.get('genes', []):
         parse_gene(gene_obj)
@@ -458,11 +453,13 @@ def expected_inheritance(variant_obj):
     return list(manual_models)
 
 
-def callers(variant_obj, category='snv'):
-    """Return info about callers."""
-    calls = [(caller['name'], variant_obj.get(caller['id']))
-             for caller in CALLERS[category] if variant_obj.get(caller['id'])]
-    return calls
+def callers(variant_obj):
+    """Return call for all callers."""
+    calls = [('GATK', variant_obj.get('gatk', 'NA')),
+             ('Samtools', variant_obj.get('samtools', 'NA')),
+             ('Freebayes', variant_obj.get('freebayes', 'NA'))]
+    existing_calls = [(name, caller) for name, caller in calls if caller]
+    return existing_calls
 
 
 def sanger(store, mail, institute_obj, case_obj, user_obj, variant_obj, sender):
diff --git a/scout/server/blueprints/variants/templates/variants/sv-variant.html b/scout/server/blueprints/variants/templates/variants/sv-variant.html
index 6a82238e0..f6b15cffb 100644
--- a/scout/server/blueprints/variants/templates/variants/sv-variant.html
+++ b/scout/server/blueprints/variants/templates/variants/sv-variant.html
@@ -222,13 +222,6 @@
         {% endfor %}
       </tbody>
     </table>
-    {% if variant.callers %}
-      <div class="panel-footer">
-        {% for name, caller in variant.callers %}
-          <span class="label label-default">{{ name }}: {{ caller }}</span>
-        {% endfor %}
-      </div>
-    {% endif %}
   </div>
 {% endmacro %}
 


Test patch:
diff --git a/tests/parse/test_frequency.py b/tests/parse/test_frequency.py
index 1712e3348..08529ae8a 100644
--- a/tests/parse/test_frequency.py
+++ b/tests/parse/test_frequency.py
@@ -1,5 +1,4 @@
-from scout.parse.variant.frequency import (parse_frequency, parse_frequencies, 
-                                           parse_sv_frequencies)
+from scout.parse.variant.frequency import parse_frequency, parse_frequencies
 
 def test_parse_frequency(cyvcf2_variant):
     # GIVEN a variant dict with a frequency in info_dict
@@ -49,16 +48,3 @@ def test_parse_frequencies(cyvcf2_variant):
     # THEN the frequencies should be returned in a dictionary
     assert frequencies['thousand_g'] == float(variant.INFO['1000GAF'])
     assert frequencies['exac'] == float(variant.INFO['EXACAF'])
-
-def test_parse_sv_frequencies_clingen_benign(cyvcf2_variant):
-    variant = cyvcf2_variant
-    # GIVEN a variant dict with a differenct frequencies
-    variant.INFO['clingen_cgh_benignAF'] = '0.01'
-    variant.INFO['clingen_cgh_benign'] = '3'
-    
-    # WHEN frequencies are parsed    
-    frequencies = parse_sv_frequencies(variant)
-    
-    # THEN the frequencies should be returned in a dictionary
-    assert frequencies['clingen_cgh_benignAF'] == float(variant.INFO['clingen_cgh_benignAF'])
-    assert frequencies['clingen_cgh_benign'] == int(variant.INFO['clingen_cgh_benign'])
diff --git a/tests/parse/test_parse_callers.py b/tests/parse/test_parse_callers.py
index 9325539b3..b68364328 100644
--- a/tests/parse/test_parse_callers.py
+++ b/tests/parse/test_parse_callers.py
@@ -64,48 +64,3 @@ def test_parse_callers_filtered_all(cyvcf2_variant):
     assert callers['gatk'] == 'Filtered'
     assert callers['freebayes'] == 'Filtered'
     assert callers['samtools'] == 'Filtered'
-
-def test_parse_sv_callers_filtered_all(cyvcf2_variant):
-    variant = cyvcf2_variant
-    variant.var_type = 'sv'
-    # GIVEN information that all callers agree on filtered
-    variant.INFO['set'] = 'FilteredInAll'
-    
-    # WHEN parsing the information
-    callers = parse_callers(variant)
-    
-    #THEN all callers should be filtered
-    assert callers['cnvnator'] == 'Filtered'
-    assert callers['delly'] == 'Filtered'
-    assert callers['tiddit'] == 'Filtered'
-    assert callers['manta'] == 'Filtered'
-
-def test_parse_sv_callers_intersection(cyvcf2_variant):
-    variant = cyvcf2_variant
-    variant.var_type = 'sv'
-    # GIVEN information that all callers agree on filtered
-    variant.INFO['set'] = 'Intersection'
-    
-    # WHEN parsing the information
-    callers = parse_callers(variant)
-    
-    #THEN all callers should be filtered
-    assert callers['cnvnator'] == 'Pass'
-    assert callers['delly'] == 'Pass'
-    assert callers['tiddit'] == 'Pass'
-    assert callers['manta'] == 'Pass'
-
-def test_parse_sv_callers_filterin_tiddit(cyvcf2_variant):
-    variant = cyvcf2_variant
-    variant.var_type = 'sv'
-    # GIVEN information that all callers agree on filtered
-    variant.INFO['set'] = 'manta-filterIntiddit'
-    
-    # WHEN parsing the information
-    callers = parse_callers(variant)
-    
-    #THEN all callers should be filtered
-    assert callers['cnvnator'] == None
-    assert callers['delly'] == None
-    assert callers['tiddit'] == 'Filtered'
-    assert callers['manta'] == 'Pass'
diff --git a/tests/parse/test_parse_coordinates.py b/tests/parse/test_parse_coordinates.py
new file mode 100644
index 000000000..791148fb3
--- /dev/null
+++ b/tests/parse/test_parse_coordinates.py
@@ -0,0 +1,253 @@
+from scout.parse.variant.coordinates import (get_cytoband_coordinates, get_sub_category, 
+                                             get_length, get_end, parse_coordinates)
+
+
+class CyvcfVariant(object):
+    """Mock a cyvcf variant
+    
+    Default is to return a variant with three individuals high genotype 
+    quality.
+    """
+    def __init__(self, chrom='1', pos=80000, ref='A', alt='C', end=None, 
+                 gt_quals=[60, 60, 60], gt_types=[1, 1, 0], var_type='snv',
+                 info_dict={}):
+        super(CyvcfVariant, self).__init__()
+        self.CHROM = chrom
+        self.POS = pos
+        self.REF = ref
+        self.ALT = [alt]
+        self.end = end or pos
+        self.gt_quals = gt_quals
+        self.gt_types = gt_types
+        self.var_type = var_type
+        self.INFO = info_dict
+
+
+def test_parse_coordinates_snv():
+    variant = CyvcfVariant()
+    
+    coordinates = parse_coordinates(variant, 'snv')
+    
+    assert coordinates['position'] == variant.POS
+
+def test_parse_coordinates_indel():
+    variant = CyvcfVariant(alt='ACCC', end=80003)
+    
+    coordinates = parse_coordinates(variant, 'snv')
+    
+    assert coordinates['position'] == variant.POS
+    assert coordinates['end'] == variant.end
+
+def test_parse_coordinates_translocation():
+    info_dict = {
+        'SVTYPE': 'BND',
+    }
+    variant = CyvcfVariant(
+        ref='N', 
+        alt='N[hs37d5:12060532[',
+        pos=724779,
+        end=724779,
+        var_type='sv',
+        info_dict=info_dict,
+    )
+    
+    coordinates = parse_coordinates(variant, 'sv')
+    
+    assert coordinates['position'] == variant.POS
+    assert coordinates['end'] == 12060532
+    assert coordinates['end_chrom'] == 'hs37d5'
+    assert coordinates['length'] == 10e10
+    assert coordinates['sub_category'] ==  'bnd'
+    
+    
+###### parse subcategory #######
+def test_get_subcategory_snv():
+    alt_len = 1
+    ref_len = 1
+    category = 'snv'
+    svtype = None
+    
+    sub_category = get_sub_category(alt_len, ref_len, category, svtype)
+    
+    assert sub_category == 'snv'
+
+def test_get_subcategory_indel():
+    alt_len = 1
+    ref_len = 3
+    category = 'snv'
+    svtype = None
+    
+    sub_category = get_sub_category(alt_len, ref_len, category, svtype)
+    
+    assert sub_category == 'indel'
+
+###### parse length #######
+
+# get_length(alt_len, ref_len, category, pos, end, svtype=None, svlen=None)
+def test_get_length_snv():
+    alt_len = 1
+    ref_len = 1
+    category = 'snv'
+    pos = end = 879537
+    
+    length = get_length(alt_len, ref_len, category, pos, end)
+    
+    assert length == 1
+
+def test_get_length_indel():
+    alt_len = 3
+    ref_len = 1
+    category = 'snv'
+    pos = end = 879537
+    
+    length = get_length(alt_len, ref_len, category, pos, end)
+    
+    assert length == 2
+
+def test_get_sv_length_small_ins():
+    ## GIVEN an insertion with whole sequence in alt field
+    alt_len = 296
+    ref_len = 1
+    category = 'sv'
+    # Pos and end is same for insertions
+    pos = end = 144343218
+    svtype = 'ins'
+    svlen = 296
+    
+    ## WHEN parsing the length
+    length = get_length(alt_len, ref_len, category, pos, end, svtype, svlen)
+    
+    ## THEN assert that the length is correct
+    assert length == 296
+
+def test_get_sv_length_large_ins_no_length():
+    ## GIVEN an imprecise insertion
+    alt_len = 5
+    ref_len = 1
+    category = 'sv'
+    # Pos and end is same for insertions
+    pos = end = 133920667
+    svtype = 'ins'
+    svlen = None
+    
+    ## WHEN parsing the length
+    length = get_length(alt_len, ref_len, category, pos, end, svtype, svlen)
+    
+    ## THEN assert that the length is correct
+    assert length == -1
+
+def test_get_sv_length_translocation():
+    ## GIVEN an translocation
+    alt_len = 16
+    ref_len = 1
+    category = 'sv'
+    pos = 726044
+    end = None
+    svtype = 'bnd'
+    svlen = None
+    
+    ## WHEN parsing the length
+    length = get_length(alt_len, ref_len, category, pos, end, svtype, svlen)
+    
+    ## THEN assert that the length is correct
+    assert length == 10e10
+
+def test_get_sv_length_cnvnator_del():
+    ## GIVEN an cnvnator type deletion
+    alt_len = 5
+    ref_len = 1
+    category = 'sv'
+    pos = 1
+    end = 10000
+    svtype = 'del'
+    svlen = -10000
+    
+    ## WHEN parsing the length
+    length = get_length(alt_len, ref_len, category, pos, end, svtype, svlen)
+    
+    ## THEN assert that the length is correct
+    assert length == 10000
+
+def test_get_sv_length_del_no_length():
+    ## GIVEN an deletion without len
+    alt_len = 5
+    ref_len = 1
+    category = 'sv'
+    pos = 869314
+    end = 870246
+    svtype = 'del'
+    svlen = None
+    
+    ## WHEN parsing the length
+    length = get_length(alt_len, ref_len, category, pos, end, svtype, svlen)
+    
+    ## THEN assert that the length is correct
+    assert length == end - pos
+
+###### parse end #######
+# get_end(pos, alt, category, snvend, svend, svlen)
+
+# snv/indels are easy since cyvcf2 are parsing the end for us
+
+def test_get_end_snv():
+    alt = 'T'
+    category = 'snv'
+    pos = snvend = 879537
+    
+    end = get_end(pos, alt, category, snvend, svend=None, svlen=None)
+    
+    assert end == snvend
+
+def test_get_end_indel():
+    alt = 'C'
+    category = 'indel'
+    pos = 302253
+    snvend = 302265
+    
+    end = get_end(pos, alt, category, snvend, svend=None, svlen=None)
+    
+    assert end == snvend
+
+# SVs are much harder since there are a lot of corner cases
+# Most SVs (except translocations) have END annotated in INFO field
+# The problem is that many times END==POS and then we have to do some magic on our own
+
+def test_get_end_tiddit_translocation():
+    ## GIVEN a translocation
+    alt = 'N[hs37d5:12060532['
+    category = 'sv'
+    pos = 724779
+    
+    ## WHEN parsing the end coordinate
+    end = get_end(pos, alt, category, snvend=None, svend=None, svlen=None)
+    
+    ## THEN assert that the end is the same as en coordinate described in alt field
+    assert end == 12060532
+
+def test_get_end_tiddit_translocation():
+    ## GIVEN a translocation
+    alt = 'N[hs37d5:12060532['
+    category = 'sv'
+    pos = 724779
+    
+    ## WHEN parsing the end coordinate
+    end = get_end(pos, alt, category, snvend=None, svend=None, svlen=None)
+    
+    ## THEN assert that the end is the same as en coordinate described in alt field
+    assert end == 12060532
+
+def test_get_end_deletion():
+    ## GIVEN a translocation
+    alt = '<DEL>'
+    category = 'sv'
+    pos = 869314
+    svend = 870246
+    svlen = None
+    
+    ## WHEN parsing the end coordinate
+    end = get_end(pos, alt, category, snvend=None, svend=svend, svlen=svlen)
+    
+    ## THEN assert that the end is the same as en coordinate described in alt field
+    assert end == svend
+
+