Commit 14b17206 authored by Hoogenboom, Jerry's avatar Hoogenboom, Jerry
Browse files

FDSTools v1.0.1.dev3

* General changes in v1.0.1:
  * Fixed crash that occurred when using the -i option to run the same command
    on multiple input files.
  * The usage string now always starts with 'fdstools', even if FDSTools was
    invoked using some other command (e.g. on Windows, FDSTools gets invoked
    through a file called 'fdstools-script.py').
  * Fixed bug with the -d/--debug option being ignored if placed before the
    tool name on systems running Python 2.7.9 or later.
  * FDSTools library files may now contain IUPAC ambiguous bases in the
    prefix and suffix sequences of STR markers (except the first sequence,
    as it is used as the reference).  Additionally, optional bases may be
    represented by lowercase letters.
  * If no explicit prefix/suffix is given for an alias, the prefix/suffix of
    the corresponding marker is assumed instead. This situation was not
    handled correctly when converting from raw sequences to TSSV or allelename
    format, which resulted in the alias remaining unused.
* Stuttermodelvis v2.0.2:
  * Added filtering option for the stutter amount (-1, +1, -2, etc.).
  * Added filtering option for the coefficient of determination (r squared
    value) of the fit functions.
* Libconvert v1.1.1:
  * Adjustments for supporting IUPAC notation in prefix and suffix sequences
    when converting from FDSTools to TSSV library format.
* Library v1.0.2:
  * Added documentation for IUPAC support to the descriptive comment of the
    [prefix] section.
parent 98c434ed
...@@ -24,7 +24,7 @@ including tools for characterisation and filtering of PCR stutter artefacts and ...@@ -24,7 +24,7 @@ including tools for characterisation and filtering of PCR stutter artefacts and
other systemic noise, and for automatic detection of the alleles in a sample. other systemic noise, and for automatic detection of the alleles in a sample.
""" """
__version_info__ = ('1', '0', '1', 'dev2') __version_info__ = ('1', '0', '1', 'dev3')
__version__ = '.'.join(__version_info__) __version__ = '.'.join(__version_info__)
usage = __doc__.split("\n\n\n") usage = __doc__.split("\n\n\n")
......
...@@ -72,9 +72,10 @@ def main(): ...@@ -72,9 +72,10 @@ def main():
""" """
Main entry point. Main entry point.
""" """
parser = argparse.ArgumentParser(formatter_class=_HelpFormatter, prog = os.path.splitext(os.path.basename(__file__))[0]
parser = argparse.ArgumentParser(formatter_class=_HelpFormatter, prog=prog,
add_help=False, description=usage[0]) add_help=False, description=usage[0])
parser.version = version(parser.prog) parser.version = version(prog)
parser.add_argument('-h', '--help', action=_HelpAction, parser.add_argument('-h', '--help', action=_HelpAction,
default=argparse.SUPPRESS, nargs=argparse.REMAINDER, default=argparse.SUPPRESS, nargs=argparse.REMAINDER,
help="show this help message, or help for the " help="show this help message, or help for the "
...@@ -100,9 +101,10 @@ def main(): ...@@ -100,9 +101,10 @@ def main():
formatter_class=_HelpFormatter, formatter_class=_HelpFormatter,
help=module.__doc__.split("\n\n", 1)[0], help=module.__doc__.split("\n\n", 1)[0],
description=module.__doc__, description=module.__doc__,
version=version(parser.prog, name, module.__version__)) version=version(prog, name, module.__version__))
__tools__[name] = subparser __tools__[name] = subparser
subparser.add_argument('-d', "--debug", action="store_true", subparser.add_argument('-d', "--debug", action="store_true",
default=argparse.SUPPRESS,
help="if specified, additional debug output is given") help="if specified, additional debug output is given")
module.add_arguments(subparser) module.add_arguments(subparser)
subparser.set_defaults(func=module.run) subparser.set_defaults(func=module.run)
......
...@@ -28,6 +28,7 @@ from StringIO import StringIO ...@@ -28,6 +28,7 @@ from StringIO import StringIO
# Patterns that match entire sequences. # Patterns that match entire sequences.
PAT_SEQ_RAW = re.compile("^[ACGT]*$") PAT_SEQ_RAW = re.compile("^[ACGT]*$")
PAT_SEQ_IUPAC = re.compile("^[ACGTUWSMKRYBDHVNacgtuwsmkrybdhvn]*$")
PAT_SEQ_TSSV = re.compile("^(?:[ACGT]+\(\d+\))*$") PAT_SEQ_TSSV = re.compile("^(?:[ACGT]+\(\d+\))*$")
PAT_SEQ_ALLELENAME_STR = re.compile( # First line: n_ACT[m] or alias. PAT_SEQ_ALLELENAME_STR = re.compile( # First line: n_ACT[m] or alias.
"^(?:(?:(?:CE)?-?\d+(?:\.\d+)?_(?:[ACGT]+\[\d+\])*)|((?!_).+?))" "^(?:(?:(?:CE)?-?\d+(?:\.\d+)?_(?:[ACGT]+\[\d+\])*)|((?!_).+?))"
...@@ -92,6 +93,17 @@ COMPL = {"A": "T", "T": "A", "U": "A", "G": "C", "C": "G", "R": "Y", "Y": "R", ...@@ -92,6 +93,17 @@ COMPL = {"A": "T", "T": "A", "U": "A", "G": "C", "C": "G", "R": "Y", "Y": "R",
"a": "t", "t": "a", "u": "a", "g": "c", "c": "g", "r": "y", "y": "r", "a": "t", "t": "a", "u": "a", "g": "c", "c": "g", "r": "y", "y": "r",
"k": "m", "m": "k", "b": "v", "v": "b", "d": "h", "h": "d"} "k": "m", "m": "k", "b": "v", "v": "b", "d": "h", "h": "d"}
# IUPAC Table of ambiguous bases.
IUPAC = {"A": "A", "C": "C", "G": "G", "T": "T", "U": "T", "W": "AT",
"S": "CG", "M": "AC", "K": "GT", "R": "AG", "Y": "CT", "B": "CGT",
"D": "AGT", "H": "ACT", "V": "ACG", "N": "ACGT",
"a": ("A", ""), "c": ("C", ""), "g": ("G", ""), "t": ("T", ""),
"u": ("T", ""), "w": ("A", "T", ""), "s": ("C", "G", ""),
"m": ("A", "C", ""), "k": ("G", "T", ""), "r": ("A", "G", ""),
"y": ("C", "T", ""), "b": ("C", "G", "T", ""),
"d": ("A", "G", "T", ""), "h": ("A", "C", "T", ""),
"v": ("A", "C", "G", ""), "n": ("A", "C", "G", "T", "")}
# Special values that may appear in the place of a sequence. # Special values that may appear in the place of a sequence.
SEQ_SPECIAL_VALUES = ("No data", "Other sequences") SEQ_SPECIAL_VALUES = ("No data", "Other sequences")
...@@ -143,7 +155,7 @@ def call_variants(template, sequence, location=("?", 1), cache=True, ...@@ -143,7 +155,7 @@ def call_variants(template, sequence, location=("?", 1), cache=True,
integer for the position, all variants are given as substitutions in integer for the position, all variants are given as substitutions in
the form posX>Y. Insertions and deletions are written as pos.1->Y the form posX>Y. Insertions and deletions are written as pos.1->Y
and posX>-, respectively. The given position is that of the first and posX>-, respectively. The given position is that of the first
base in the template. With the location set to "suffix", a plus base in the template. With the location set to "suffix", a plus
sign is prepended to position numbers and the first base in the sign is prepended to position numbers and the first base in the
template is pos=1. With location set to "prefix", a minus sign is template is pos=1. With location set to "prefix", a minus sign is
prepended and bases are counted from right to left instead. prepended and bases are counted from right to left instead.
...@@ -422,6 +434,21 @@ def mutate_sequence(seq, variants, location=None): ...@@ -422,6 +434,21 @@ def mutate_sequence(seq, variants, location=None):
#mutate_sequence #mutate_sequence
def iupac_expand_ambiguous(seq):
"""Return generator for all possible values of ambiguous seq."""
return ("".join(x) for x in itertools.product(
*((b for b in IUPAC[a]) for a in seq)))
#iupac_expand_ambiguous
def iupac_pattern_ambiguous(seq):
"""Return regex pattern that matches the ambiguous seq."""
return "".join(
(("[%s]" if len(IUPAC[x.upper()]) > 1 else "%s") +
("?" if x.islower() else "")) % IUPAC[x.upper()] for x in seq)
#iupac_expand_ambiguous
def parse_library(libfile, stream=False): def parse_library(libfile, stream=False):
try: try:
if not stream: if not stream:
...@@ -483,6 +510,7 @@ def parse_library_tsv(handle): ...@@ -483,6 +510,7 @@ def parse_library_tsv(handle):
library["blocks_middle"][marker] = [ library["blocks_middle"][marker] = [
(block[0], int(block[1]), int(block[2])) for block in (block[0], int(block[1]), int(block[2])) for block in
PAT_STR_DEF_BLOCK.findall(line[3])] PAT_STR_DEF_BLOCK.findall(line[3])]
# NOTE: Libconvert tool expects "(seq){num,num}" blocks ONLY!
library["regex"][marker] = re.compile( library["regex"][marker] = re.compile(
"".join(("^", "".join( "".join(("^", "".join(
"(%s){%s,%s}" % x for x in PAT_STR_DEF_BLOCK.findall(line[3])), "(%s){%s,%s}" % x for x in PAT_STR_DEF_BLOCK.findall(line[3])),
...@@ -533,11 +561,12 @@ def parse_library_ini(handle): ...@@ -533,11 +561,12 @@ def parse_library_ini(handle):
raise ValueError( raise ValueError(
"A prefix was defined for non-STR marker %s" % marker) "A prefix was defined for non-STR marker %s" % marker)
values = PAT_SPLIT.split(value) values = PAT_SPLIT.split(value)
for value in values: for i in range(len(values)):
if PAT_SEQ_RAW.match(value) is None: if (not i and PAT_SEQ_RAW.match(values[i]) is None or
i and PAT_SEQ_IUPAC.match(values[i]) is None):
raise ValueError( raise ValueError(
"Prefix sequence '%s' of marker %s is invalid" % "Prefix sequence '%s' of marker %s is invalid" %
(value, marker)) (values[i], marker))
library["prefix"][marker] = values library["prefix"][marker] = values
markers.add(marker) markers.add(marker)
elif section_low == "suffix": elif section_low == "suffix":
...@@ -545,11 +574,12 @@ def parse_library_ini(handle): ...@@ -545,11 +574,12 @@ def parse_library_ini(handle):
raise ValueError( raise ValueError(
"A suffix was defined for non-STR marker %s" % marker) "A suffix was defined for non-STR marker %s" % marker)
values = PAT_SPLIT.split(value) values = PAT_SPLIT.split(value)
for value in values: for i in range(len(values)):
if PAT_SEQ_RAW.match(value) is None: if (not i and PAT_SEQ_RAW.match(values[i]) is None or
i and PAT_SEQ_IUPAC.match(values[i]) is None):
raise ValueError( raise ValueError(
"Suffix sequence '%s' of marker %s is invalid" % "Suffix sequence '%s' of marker %s is invalid" %
(value, marker)) (values[i], marker))
library["suffix"][marker] = values library["suffix"][marker] = values
markers.add(marker) markers.add(marker)
elif section_low == "genome_position": elif section_low == "genome_position":
...@@ -692,20 +722,31 @@ def parse_library_ini(handle): ...@@ -692,20 +722,31 @@ def parse_library_ini(handle):
(marker, reflength, length)) (marker, reflength, length))
# Compile regular expressions. # Compile regular expressions.
# NOTE: The libconvert tool expects "(seq){num,num}" blocks ONLY!
for marker in markers: for marker in markers:
parts = [] parts = []
blocksm = [] blocksm = []
if marker in library["prefix"]: if marker in library["prefix"]:
parts += ("(%s){0,1}" % x for x in library["prefix"][marker]) parts += ("(%s){0,1}" % iupac_pattern_ambiguous(x)
for x in library["prefix"][marker])
elif (marker in library["aliases"] and
library["aliases"][marker]["marker"] in library["prefix"]):
parts += ("(%s){0,1}" % iupac_pattern_ambiguous(x)
for x in library["prefix"][
library["aliases"][marker]["marker"]])
if marker in library["aliases"]: if marker in library["aliases"]:
blocksm.append((library["aliases"][marker]["sequence"], 0, 1)) blocksm.append((library["aliases"][marker]["sequence"], 0, 1))
elif marker in library["regex"]: elif marker in library["regex"]:
blocksm += [(block[0], int(block[1]), int(block[2])) for block in blocksm += [(block[0], int(block[1]), int(block[2])) for block in
PAT_STR_DEF_BLOCK.findall(library["regex"][marker])] PAT_STR_DEF_BLOCK.findall(library["regex"][marker])]
parts += ["(%s){%s,%s}" % x for x in blocksm] parts += ("(%s){%s,%s}" % x for x in blocksm)
if marker in library["suffix"]: if marker in library["suffix"]:
parts += ("(%s){0,1}" % x for x in library["suffix"][marker]) parts += ("(%s){0,1}" % iupac_pattern_ambiguous(x)
for x in library["suffix"][marker])
elif (marker in library["aliases"] and
library["aliases"][marker]["marker"] in library["suffix"]):
parts += ("(%s){0,1}" % iupac_pattern_ambiguous(x)
for x in library["suffix"][
library["aliases"][marker]["marker"]])
if parts: if parts:
library["regex"][marker] = re.compile( library["regex"][marker] = re.compile(
"".join(["^"] + parts + ["$"])) "".join(["^"] + parts + ["$"]))
...@@ -912,8 +953,7 @@ def convert_sequence_raw_tssv(seq, library, marker, return_alias=False): ...@@ -912,8 +953,7 @@ def convert_sequence_raw_tssv(seq, library, marker, return_alias=False):
match = None match = None
if "aliases" in library: if "aliases" in library:
for alias in library["aliases"]: for alias in library["aliases"]:
if (library["aliases"][alias]["marker"] == marker and if library["aliases"][alias]["marker"] == marker:
alias in library["regex"]):
match = library["regex"][alias].match(seq) match = library["regex"][alias].match(seq)
if match is not None: if match is not None:
marker = alias marker = alias
...@@ -930,13 +970,15 @@ def convert_sequence_raw_tssv(seq, library, marker, return_alias=False): ...@@ -930,13 +970,15 @@ def convert_sequence_raw_tssv(seq, library, marker, return_alias=False):
# Find explictily provided prefix and/or suffix if present. # Find explictily provided prefix and/or suffix if present.
pre_suf = ["", ""] pre_suf = ["", ""]
if "prefix" in library and marker in library["prefix"]: if "prefix" in library and marker in library["prefix"]:
for prefix in library["prefix"][marker]: for prefix in (x for x in library["prefix"][marker]
for x in iupac_expand_ambiguous(x)):
if seq.startswith(prefix): if seq.startswith(prefix):
pre_suf[0] = prefix pre_suf[0] = prefix
seq = seq[len(prefix):] seq = seq[len(prefix):]
break break
if "suffix" in library and marker in library["suffix"]: if "suffix" in library and marker in library["suffix"]:
for suffix in library["suffix"][marker]: for suffix in (x for x in library["suffix"][marker]
for x in iupac_expand_ambiguous(x)):
if seq.endswith(suffix): if seq.endswith(suffix):
pre_suf[1] = suffix pre_suf[1] = suffix
seq = seq[:-len(suffix)] seq = seq[:-len(suffix)]
...@@ -1695,8 +1737,8 @@ def get_input_output_files(args, single=False, batch_support=False): ...@@ -1695,8 +1737,8 @@ def get_input_output_files(args, single=False, batch_support=False):
# Each yielded tuple should cause a separate run of the tool. # Each yielded tuple should cause a separate run of the tool.
# Glob args.infiles in case the shell didn't (e.g, on Windows). # Glob args.infiles in case the shell didn't (e.g, on Windows).
infiles = [x for x in infiles for x in glob_path(x)] if "infiles" in \ infiles = [x for x in args.infiles for x in glob_path(x)] if "infiles"\
args and args.infiles is not None else [args.infile] in args and args.infiles is not None else [args.infile]
if infiles == ["-"] and sys.stdin.isatty(): if infiles == ["-"] and sys.stdin.isatty():
return False # No input specified. return False # No input specified.
......
...@@ -44,15 +44,13 @@ import sys ...@@ -44,15 +44,13 @@ import sys
import re import re
import os.path import os.path
from ..lib import parse_library, INI_COMMENT from ..lib import parse_library, iupac_expand_ambiguous, INI_COMMENT
from library import make_empty_library_ini from library import make_empty_library_ini
from ConfigParser import RawConfigParser
__version__ = "1.1.0" __version__ = "1.1.1"
def convert_library(infile, outfile, aliases=False): def convert_library(infile, outfile, aliases=False):
pattern_reverse = re.compile("\(([ACGT]+)\)\{(\d+),(\d+)\}")
if infile is not None: if infile is not None:
library = parse_library(infile, stream=True) library = parse_library(infile, stream=True)
else: else:
...@@ -94,19 +92,30 @@ def convert_library(infile, outfile, aliases=False): ...@@ -94,19 +92,30 @@ def convert_library(infile, outfile, aliases=False):
library["aliases"][marker]["marker"]] library["aliases"][marker]["marker"]]
else: else:
continue # Worthless, no flanks. continue # Worthless, no flanks.
if marker in library["regex"]: if marker in library["prefix"]:
pattern = pattern_reverse.findall( pattern.append((library["prefix"][marker][0], 0, 1))
library["regex"][marker].pattern) elif library["aliases"][marker]["marker"] in library["prefix"]:
pattern.append((library["prefix"][
library["aliases"][marker]["marker"]][0], 0, 1))
pattern.append((library["aliases"][marker]["sequence"], 0, 1))
if marker in library["suffix"]:
pattern.append((library["suffix"][marker][0], 0, 1))
elif library["aliases"][marker]["marker"] in library["suffix"]:
pattern.append((library["suffix"][
library["aliases"][marker]["marker"]][0], 0, 1))
elif aliases or marker not in marker_aliases: elif aliases or marker not in marker_aliases:
# Normal marker, or separately from its aliases. # Normal marker, or separately from its aliases.
if marker not in library["flanks"]: if marker not in library["flanks"]:
continue # Worthless, no flanks. continue # Worthless, no flanks.
flanks = library["flanks"][marker] flanks = library["flanks"][marker]
if marker in library["regex"]: if marker in library["prefix"]:
pattern = pattern_reverse.findall( pattern += ((x, 0, 1) for x in library["prefix"][marker])
library["regex"][marker].pattern) if marker in library["blocks_middle"]:
elif marker in library["nostr_reference"]: pattern += (library["blocks_middle"][marker])
pattern = [(library["nostr_reference"][marker], "1", "1")] if marker in library["suffix"]:
pattern += ((x, 0, 1) for x in library["suffix"][marker])
if marker in library["nostr_reference"]:
pattern.append((library["nostr_reference"][marker], 1, 1))
else: else:
# Merge marker with its aliases. # Merge marker with its aliases.
flanks = False flanks = False
...@@ -120,16 +129,11 @@ def convert_library(infile, outfile, aliases=False): ...@@ -120,16 +129,11 @@ def convert_library(infile, outfile, aliases=False):
if not flanks: if not flanks:
continue # Worthless, no flanks. continue # Worthless, no flanks.
prefixes = []
suffixes = []
if marker in library["prefix"]:
prefixes += library["prefix"][marker]
if marker in library["suffix"]:
suffixes += library["suffix"][marker]
middle = [] middle = []
if marker in library["regex"]: if marker in library["regex"]:
# This marker has a regex next to its aliases. if marker in library["blocks_middle"]:
middle += library["blocks_middle"][marker]
# Check if the aliases fit the regex without change. # Check if the aliases fit the regex without change.
unmatched = [] unmatched = []
for alias in marker_aliases[marker]: for alias in marker_aliases[marker]:
...@@ -143,20 +147,21 @@ def convert_library(infile, outfile, aliases=False): ...@@ -143,20 +147,21 @@ def convert_library(infile, outfile, aliases=False):
if library["regex"][marker].match(allele) is None: if library["regex"][marker].match(allele) is None:
unmatched.append( unmatched.append(
library["aliases"][alias]["sequence"]) library["aliases"][alias]["sequence"])
middle = pattern_reverse.findall(
library["regex"][marker].pattern)[len(prefixes):]
if len(suffixes):
middle = middle[:-len(suffixes)]
if unmatched: if unmatched:
middle = [(x, "0", "1") for x in unmatched] + \ middle = [(x, 0, 1) for x in unmatched] + \
[(x[0], "0", x[2]) for x in middle] [(x[0], 0, x[2]) for x in middle]
elif marker in library["nostr_reference"]: elif marker in library["nostr_reference"]:
middle = [(library["nostr_reference"][marker], middle.append((library["nostr_reference"][marker],
"0" if marker in marker_aliases else "1", "1")] 0 if marker in marker_aliases else 1, 1))
# Add prefixes and suffixes of aliases. # Gather prefixes and suffixes including aliases.
prefixes = []
suffixes = []
if marker in library["prefix"]:
prefixes += library["prefix"][marker]
if marker in library["suffix"]:
suffixes += library["suffix"][marker]
if marker in marker_aliases: if marker in marker_aliases:
for alias in marker_aliases[marker]: for alias in marker_aliases[marker]:
if alias in library["prefix"]: if alias in library["prefix"]:
...@@ -168,22 +173,25 @@ def convert_library(infile, outfile, aliases=False): ...@@ -168,22 +173,25 @@ def convert_library(infile, outfile, aliases=False):
library["nostr_reference"][marker] != library["nostr_reference"][marker] !=
library["aliases"][alias]["sequence"]): library["aliases"][alias]["sequence"]):
middle.append(( middle.append((
library["aliases"][alias]["sequence"], library["aliases"][alias]["sequence"], 0, 1))
"0", "1"))
# Final regex is prefixes + middle + suffixes. # Final regex is prefixes + middle + suffixes.
pattern = []
for i in range(len(prefixes)): for i in range(len(prefixes)):
if i == prefixes.index(prefixes[i]): if i == prefixes.index(prefixes[i]):
pattern.append((prefixes[i], "0", "1")) pattern.append((prefixes[i], 0, 1))
pattern += middle pattern += middle
for i in range(len(suffixes)): for i in range(len(suffixes)):
if i == suffixes.index(suffixes[i]): if i == suffixes.index(suffixes[i]):
pattern.append((suffixes[i], "0", "1")) pattern.append((suffixes[i], 0, 1))
outfile.write("%s\t%s\t%s\t%s\n" % ( # Write marker to outfile.
marker, flanks[0], flanks[1], outfile.write("%s\t%s\t%s\t" % (marker, flanks[0], flanks[1]))
" ".join("%s %s %s" % x for x in pattern))) space = ""
for p in pattern:
for s in iupac_expand_ambiguous(p[0]):
outfile.write(space + "%s %i %i" % (s, p[1], p[2]))
space = " "
outfile.write("\n")
else: else:
# TSSV -> FDSTools # TSSV -> FDSTools
...@@ -192,6 +200,7 @@ def convert_library(infile, outfile, aliases=False): ...@@ -192,6 +200,7 @@ def convert_library(infile, outfile, aliases=False):
ini = make_empty_library_ini("full", aliases) ini = make_empty_library_ini("full", aliases)
# Enter flanking sequences and STR definitions. # Enter flanking sequences and STR definitions.
pattern_reverse = re.compile("\(([ACGT]+)\)\{(\d+),(\d+)\}")
fmt = "%%-%is" % reduce(max, map(len, fmt = "%%-%is" % reduce(max, map(len,
set(library["flanks"].keys() + library["regex"].keys())), 0) set(library["flanks"].keys() + library["regex"].keys())), 0)
for marker in sorted(library["flanks"]): for marker in sorted(library["flanks"]):
......
...@@ -57,7 +57,7 @@ from ..lib import INI_COMMENT ...@@ -57,7 +57,7 @@ from ..lib import INI_COMMENT
from ConfigParser import RawConfigParser from ConfigParser import RawConfigParser
from types import MethodType from types import MethodType
__version__ = "1.0.1" __version__ = "1.0.2"
def ini_add_comment(ini, section, comment): def ini_add_comment(ini, section, comment):
...@@ -112,7 +112,10 @@ def make_empty_library_ini(type, aliases=False): ...@@ -112,7 +112,10 @@ def make_empty_library_ini(type, aliases=False):
"flank and the repeat and is omitted from allele names. The first " "flank and the repeat and is omitted from allele names. The first "
"sequence listed is used as the reference sequence for that " "sequence listed is used as the reference sequence for that "
"marker when generating allele names. Deviations from the " "marker when generating allele names. Deviations from the "
"reference are expressed as variants.") "reference are expressed as variants. IUPAC notation for "
"ambiguous bases (e.g., 'R' for 'A or G') is supported, except "
"for the first sequence given. Lowercase letters represent "
"optional bases.")
ini.set("prefix", ";CSF1P0 = CTAAGTACTTC") ini.set("prefix", ";CSF1P0 = CTAAGTACTTC")
ini.add_section("suffix") ini.add_section("suffix")
ini.add_comment("suffix", ini.add_comment("suffix",
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Stutter Model Visualisation - FDSTools</title> <title>Stutter Model Visualisation - FDSTools</title>
<!-- VERSION 2.0.0 --> <!-- VERSION 2.0.2 -->
<!-- BEGIN_LIBRARIES --> <!-- BEGIN_LIBRARIES -->
<script src="https://vega.github.io/vega-editor/vendor/d3.min.js"></script> <script src="https://vega.github.io/vega-editor/vendor/d3.min.js"></script>
<script src="https://vega.github.io/vega/vega.min.js"></script> <script src="https://vega.github.io/vega/vega.min.js"></script>
...@@ -451,11 +451,15 @@ ...@@ -451,11 +451,15 @@
<span class="help" title="Type AG to show all repeat units containing 'AG'. Type =AG to show the 'AG' repeat unit only. Separate multiple values with spaces.">?</span> <span class="help" title="Type AG to show all repeat units containing 'AG'. Type =AG to show the 'AG' repeat unit only. Separate multiple values with spaces.">?</span>
</label> </label>
</td> </td>
</tr><!-- </tr>
<tr> <tr>
<td><label for="foldFilter">Stutter amount</label></td> <td><label for="foldSelect">Stutter amount</label></td>
<td><input type="text" value="" id="foldFilter" size="3"></td> <td>
</tr>--> <select id="foldSelect">
<option value="" class="special-option" selected>Show all</option>
</select>
</td>
</tr>
<tr> <tr>
<td><label for="markerFilter">Marker name</label></td> <td><label for="markerFilter">Marker name</label></td>
<td> <td>
...@@ -465,6 +469,15 @@ ...@@ -465,6 +469,15 @@
</label> </label>
</td> </td>
</tr> </tr>
<tr>
<td><label for="r2Filter">Minimum <i>r</i><sup>2</sup></label></td>
<td>
<label>
<input type="text" id="r2Filter" size="20">
<span class="help" title="R squared, the coefficient of determination, is 1 if the fit explains the data as good as possible and lower otherwise.">?</span>
</label>
</td>
</tr>
<tr> <tr>
<td><label for="allDataFilter">Fit to all data</label></td> <td><label for="allDataFilter">Fit to all data</label></td>
<td><label><input type="checkbox" id="allDataFilter" checked> show</label></td> <td><label><input type="checkbox" id="allDataFilter" checked> show</label></td>
...@@ -642,6 +655,37 @@ function makeUnitSelectionOptions(data){ ...@@ -642,6 +655,37 @@ function makeUnitSelectionOptions(data){
} }
} }
function makeFoldSelectionOptions(data){
var sel = document.getElementById("foldSelect");
var val = sel.value;
removeChildren(sel);
var opt = document.createElement("option");
opt.setAttribute("value", "");
opt.setAttribute("class", "special-option");
opt.innerText = "Show all";
sel.appendChild(opt);
var folds = {}