Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the configuration dictionary for igv.js #18

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions ipyigv/ipyigv.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

from .options import *

from .utils import widget_serialization_no_none
from ._version import EXTENSION_VERSION

PUBLIC_GENOMES_FILE = os.path.join(os.path.dirname(__file__), 'public_genomes.json')
Expand Down Expand Up @@ -47,23 +46,23 @@ def __init__(self, **kwargs):
# Widget properties are defined as traitlets. Any property tagged with `sync=True`
# is automatically synced to the frontend *any* time it changes in Python.
# It is synced back to Python from the frontend *any* time the model is touched.
genome = InstanceDict(ReferenceGenome).tag(sync=True, **widget_serialization_no_none)
tracks = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none)
genome = InstanceDict(ReferenceGenome).tag(sync=True, **widget_serialization)
tracks = List(InstanceDict(Track)).tag(sync=True, **widget_serialization)
doubleClickDelay = Int(default_value=500).tag(sync=True)
flanking = Int(default_value=1000).tag(sync=True)
genomeList = Unicode(allow_none=True).tag(sync=True, **widget_serialization_no_none) # optional URL
locus = (Unicode() | List(Unicode())).tag(sync=True, **widget_serialization_no_none)
genomeList = Unicode(allow_none=True).tag(sync=True, **widget_serialization) # optional URL
locus = (Unicode() | List(Unicode())).tag(sync=True, **widget_serialization)
minimumBases = Int(default_value=40).tag(sync=True)
queryParametersSupported = Bool(default=False).tag(sync=True)
search = InstanceDict(SearchService, allow_none=True).tag(sync=True, **widget_serialization_no_none)
search = InstanceDict(SearchService, allow_none=True).tag(sync=True, **widget_serialization)
showAllChromosomes = Bool(default_value=True).tag(sync=True)
showAllChromosomeWidget = Bool(default_value=True).tag(sync=True)
showNavigation = Bool(default_value=True).tag(sync=True)
showSVGButton = Bool(default_value=False).tag(sync=True)
showRuler = Bool(default_value=True).tag(sync=True)
showCenterGuide = Bool(default_value=False).tag(sync=True)
# trackDefaults = # missing documentation
roi = List(InstanceDict(AnnotationTrack)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(AnnotationTrack)).tag(sync=True, **widget_serialization) # regions of interest
oauthToken = Unicode(allow_none = True).tag(sync=True)
apiKey = Unicode(allow_none = True).tag(sync=True)
clientId = Unicode(allow_none = True).tag(sync=True)
Expand Down
49 changes: 24 additions & 25 deletions ipyigv/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from ipywidgets.widgets.trait_types import Color, InstanceDict
from ipywidgets.widgets import widget

from .utils import widget_serialization_no_none
from ._version import EXTENSION_VERSION


Expand Down Expand Up @@ -143,11 +142,11 @@ class AnnotationTrack(Track):
nameField = Unicode(default_value = 'Name').tag(sync=True)
maxRows = Int (default_value = 500).tag(sync=True)
searchable = Bool(default_value=False).tag(sync=True)
filterTypes = List(Unicode, default_value=['chromosone', 'gene']).tag(sync=True, **widget_serialization_no_none)
filterTypes = List(Unicode, default_value=['chromosone', 'gene']).tag(sync=True, **widget_serialization)
color = Color("rgb(0,0,150)").tag(sync=True)
altColor = Color("rgb(0,0,150)").tag(sync=True)
colorBy = Instance(FieldColors, allow_none=True).tag(sync=True, **widget_serialization_no_none)
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
colorBy = Instance(FieldColors, allow_none=True).tag(sync=True, **widget_serialization)
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand Down Expand Up @@ -175,16 +174,16 @@ class AlignmentTrack(Track):
samplingDepth = Int(100).tag(sync=True)
alignmentRowHeight = Int(14).tag(sync=True)
readgroup = Unicode("RG").tag(sync=True)
sortOption = Instance(SortOption, allow_none=True).tag(sync=True, **widget_serialization_no_none)
sortOption = Instance(SortOption, allow_none=True).tag(sync=True, **widget_serialization)
showSoftClips = Bool(False).tag(sync=True)
showMismatches = Bool(True).tag(sync=True)

# Paired-end and mate-pair coloring options.
pairOrientation = Unicode(allow_none=True).tag(sync=True, **widget_serialization_no_none) # ff, fr, or rf
minFragmentLength = Int(allow_none=True).tag(sync=True, **widget_serialization_no_none)
maxFragmentLength = Int(allow_none=True).tag(sync=True, **widget_serialization_no_none)
pairOrientation = Unicode(allow_none=True).tag(sync=True, **widget_serialization) # ff, fr, or rf
minFragmentLength = Int(allow_none=True).tag(sync=True, **widget_serialization)
maxFragmentLength = Int(allow_none=True).tag(sync=True, **widget_serialization)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand All @@ -203,7 +202,7 @@ class VariantTrack(Track):
squishedCallHeight = Int(1).tag(sync=True)
expandedCallHeight = Int(10).tag(sync=True)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


class Guideline(HasTraits):
Expand All @@ -221,14 +220,14 @@ class WigTrack(Track):

type = Unicode('wig', read_only=True).tag(sync=True)
autoscale = Bool(True).tag(sync=True)
autoscaleGroup = Unicode(allow_none=True).tag(sync=True, **widget_serialization_no_none)
autoscaleGroup = Unicode(allow_none=True).tag(sync=True, **widget_serialization)
min = Int(0).tag(sync=True)
max = Int(allow_none=True).tag(sync=True, **widget_serialization_no_none)
max = Int(allow_none=True).tag(sync=True, **widget_serialization)
color = Color(default_value="rgb(150, 150, 150)").tag(sync=True)
altColor = Color(allow_none=True).tag(sync=True, **widget_serialization_no_none)
guideLines = List(trait=Instance(Guideline), allow_none=True).tag(sync=True, **widget_serialization_no_none)
altColor = Color(allow_none=True).tag(sync=True, **widget_serialization)
guideLines = List(trait=Instance(Guideline), allow_none=True).tag(sync=True, **widget_serialization)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand All @@ -239,11 +238,11 @@ class SegTrack(Track):
"""

type = Unicode('seg', read_only=True).tag(sync=True)
isLog = Bool(allow_none=True).tag(sync=True, **widget_serialization_no_none)
isLog = Bool(allow_none=True).tag(sync=True, **widget_serialization)
displayMode = Unicode("EXPANDED").tag(sync=True) # "EXPANDED", "SQUISHED", or "FILL"
sort = InstanceDict(SortOrder).tag(sync=True, **widget_serialization)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand All @@ -263,19 +262,19 @@ class SpliceJunctionsTrack(Track):
labelMultiMappedReadCount = Bool(True).tag(sync=True)
labelTotalReadCount = Bool(False).tag(sync=True)
labelMotif = Bool(False).tag(sync=True)
labelAnnotatedJunction = Unicode(allow_none=True).tag(sync=True, **widget_serialization_no_none)
labelAnnotatedJunction = Unicode(allow_none=True).tag(sync=True, **widget_serialization)

# Filtering Options
minUniquelyMappedReads = Int(0).tag(sync=True)
minTotalReads = Int(0).tag(sync=True)
maxFractionMultiMappedReads = Int(1).tag(sync=True)
minSplicedAlignmentOverhang = Int(0).tag(sync=True)
hideStrand = Unicode(allow_none=True).tag(sync=True, **widget_serialization_no_none) # None, "+" or "-"
hideStrand = Unicode(allow_none=True).tag(sync=True, **widget_serialization) # None, "+" or "-"
hideAnnotatedJunctions = Bool(False).tag(sync=True)
hideUnannotatedJunctions = Bool(False).tag(sync=True)
hideMotifs = List(Unicode).tag(sync=True, **widget_serialization)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand All @@ -291,9 +290,9 @@ class GwasTrack (Track):
# format = Unicode().tag(sync=True) # 'bed' or 'gwas' - format is already in Track -> validation only
posteriorProbability = Bool(False).tag(sync=True)
dotSize = Int(3).tag(sync=True)
columns = Dict(key_trait=Unicode, value_trait=Int, allow_none=True).tag(sync=True, **widget_serialization_no_none)
columns = Dict(key_trait=Unicode, value_trait=Int, allow_none=True).tag(sync=True, **widget_serialization)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


@register
Expand All @@ -307,7 +306,7 @@ class InteractionTrack (Track):
arcOrientation = Bool(True).tag(sync=True)
thickness = Int(2).tag(sync=True)

roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none) # regions of interest
roi = List(InstanceDict(Track)).tag(sync=True, **widget_serialization) # regions of interest


class Exon(HasTraits):
Expand Down Expand Up @@ -357,8 +356,8 @@ class ReferenceGenome(Widget):
indexURL = Unicode(allow_none=True).tag(sync=True)
cytobandURL = Unicode(allow_none=True).tag(sync=True)
aliasURL = Unicode(allow_none=True).tag(sync=True)
indexed = Bool(default_value=False).tag(sync=True)
tracks = List(InstanceDict(Track)).tag(sync=True, **widget_serialization_no_none)
indexed = Bool(allow_none=True, default_value=None).tag(sync=True)
tracks = List(InstanceDict(Track)).tag(sync=True, **widget_serialization)
chromosomeOrder = Unicode(allow_none=True).tag(sync=True)
headers = Dict().tag(sync=True)
wholeGenomeView = Bool(default_value=True).tag(sync=True)
Expand Down
12 changes: 10 additions & 2 deletions ipyigv/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
from ipywidgets import Widget, widget_serialization

def is_json_valid(v):
if v is None:
return False
elif isinstance(v, str):
return (v != "")
else:
return True


def _widget_to_json_no_none(x, obj):
if isinstance(x, dict):
return {k: _widget_to_json_no_none(v, obj) for k, v in x.items() if v is not None}
return {k: _widget_to_json_no_none(v, obj) for k, v in x.items() if v not in (None, "")}
elif isinstance(x, (list, tuple)):
return [_widget_to_json_no_none(v, obj) for v in x if v is not None]
return [_widget_to_json_no_none(v, obj) for v in x if v not in (None, "")]
elif isinstance(x, Widget):
return "IPY_MODEL_" + x.model_id
else:
Expand Down
3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
"@jupyterlab/builder": "^3.0.0",
"css-loader": "^5.0.1",
"rimraf": "^2.6.1",
"schema-utils": "2.6.6",
"style-loader": "^2.0.0",
"webpack": "^4.44.2",
"webpack": "^4.46.0",
"webpack-cli": "^3.1.2"
},
"jupyterlab": {
Expand Down
130 changes: 66 additions & 64 deletions js/src/ipyigv.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import '../css/widget.css'

import { MODULE_NAME, MODULE_VERSION } from './version'

// When serialiazing the entire widget state for embedding, only values that
// differ from the defaults will be specified.


export class TrackModel extends widgets.WidgetModel {
defaults () {
return _.extend(super.defaults(), {
Expand All @@ -22,6 +18,21 @@ export class TrackModel extends widgets.WidgetModel {
_view_module_version : MODULE_VERSION,
});
};

// returns a dictionary representing a track which igv.js can take in its configuration
to_dict(include_empty = false) {
let keys = Object.keys(this.attributes).filter(k => !k.startsWith("_"))
// removing empty values unless specified otherwise
.filter(k=> (include_empty || !([null, undefined, ""].includes(this.get(k)))));

var dict = keys.reduce((result, key)=> ({...result, [key]:this.get(key)}), {});

if (keys.includes('roi')) {
dict['roi'] = dict['roi'].map(track => track.to_dict(include_empty));
};
console.log ("Finished generating dictionary from TrackModel", dict);
return dict ;
};
};


Expand All @@ -36,6 +47,23 @@ export class ReferenceGenomeModel extends widgets.WidgetModel {
_view_module_version : MODULE_VERSION,
});
};

// returns a dictionary representing a genome which igv.js can take in its configuration
to_dict(include_empty = false) {
let keys = Object.keys(this.attributes).filter(k => !k.startsWith("_"))
// removing empty values unless specified otherwise
.filter(k=> (include_empty || !([null, undefined, ""].includes(this.get(k)))));

var dict = keys.reduce((result, key)=> ({...result, [key]:this.get(key)}), {});

// special treatment for tracks
if (keys.includes('tracks')) {
let tracks = dict['tracks'];
dict['tracks'] = tracks.map(track=> track.to_dict(include_empty));
};
console.log ("Finished generating dictionary from ReferenceGenomeView", dict);
return dict ;
};
};

ReferenceGenomeModel.serializers = _.extend({
Expand Down Expand Up @@ -71,6 +99,39 @@ export class IgvModel extends widgets.DOMWidgetModel {
this.trigger('search', symbol);
}
};

// returns a dictionary representing a genome which igv.js can take in its configuration
to_dict(include_empty = false) {
var keys = Object.keys(this.attributes).filter(k => !k.startsWith("_"))
// removing the layout attribute -- not related to igv
.filter(k=> (k!='layout'))
// removing empty values unless specified otherwise
.filter(k=> (include_empty || !([null, undefined, ""].includes(this.get(k)))));

var dict = keys.reduce((result, key)=> ({...result, [key]:this.get(key)}), {});

// special treatment for tracks
if (keys.includes('tracks')) {
let tracks = dict['tracks'];
dict['tracks'] = tracks.map(track=> track.to_dict(include_empty));
};

// special treatment for regions of interest
if (keys.includes('roi')) {
let roi = dict['roi'];
dict['roi'] = roi.map(roi=> roi.to_dict(include_empty));
};


// special treatment for genome
if (keys.includes('genome')) { // should always be the case
let genome = dict['genome'];
dict['genome'] = genome.to_dict(include_empty);
};

console.log ("Finished generating dictionary from IgvModel", dict);
return dict ;
};
};

IgvModel.serializers = _.extend({
Expand Down Expand Up @@ -117,58 +178,8 @@ export class IgvBrowser extends widgets.DOMWidgetView {
super.render();

this.igvDiv = document.createElement("div");
var referenceGenome = this.model.get('genome');
var tracks = this.model.get('tracks');
var roi = this.model.get('roi');
var doubleClickDelay = this.model.get('doubleClickDelay');
var flanking = this.model.get('flanking');
var genomeList = this.model.get('genomeList');
var locus = this.model.get('locus');
var minimumBases = this.model.get('minimumBases');
var queryParametersSupported = this.model.get('queryParametersSupported');
var search = this.model.get('search');
var showAllChromosomes = this.model.get('showAllChromosomes');
var showAllChromosomeWidget = this.model.get('showAllChromosomeWidget');
var showNavigation = this.model.get('showNavigation');
var showSVGButton = this.model.get('showSVGButton');
var showRuler = this.model.get('showRuler');
var showCenterGuide = this.model.get('showCenterGuide');
var oauthToken = this.model.get('oauthToken');
var apiKey = this.model.get('apiKey');
var clientId = this.model.get('clientId');


var options = {
reference: referenceGenome,
tracks: tracks,
roi: roi,
doubleClickDelay: doubleClickDelay,
flanking: flanking,
genomeList: genomeList,
locus: locus,
minimumBases: minimumBases,
queryParametersSupported: queryParametersSupported,
showAllChromosomes:showAllChromosomes,
showAllChromosomeWidget: showAllChromosomeWidget,
showNavigation: showNavigation,
showSVGButton: showSVGButton,
showRuler: showRuler,
showCenterGuide: showCenterGuide,
};

if (search) {
options['search']=search
}
if (oauthToken) {
options['oauthToken']=oauthToken
}
if (apiKey) {
options['apiKey']=apiKey
}
if (clientId) {
options['clientId']=clientId
}

var options = this.model.to_dict()
console.log("rendering browser", options);
this.browser = igv.createBrowser(this.igvDiv, options)
.then((browser) => {
Expand Down Expand Up @@ -314,12 +325,3 @@ export class IgvBrowser extends widgets.DOMWidgetView {


}

// module.exports = {
// IgvModel: IgvModel,
// IgvBrowser: IgvBrowser,
// ReferenceGenomeView: ReferenceGenomeView,
// ReferenceGenomeModel: ReferenceGenomeModel,
// TrackView: TrackView,
// TrackModel: TrackModel,
// };
Loading