Mercurial > repos > fubar > jbrowse2
changeset 73:3b2815efa5d9 draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/jbrowse2 commit b361309b05a861da9b64e1324157a8c32767e0bf
author | fubar |
---|---|
date | Mon, 01 Apr 2024 03:41:42 +0000 |
parents | 2bdb748df098 |
children | 13ede71c3a4b |
files | config.json jbrowse2.py jbrowse2.xml |
diffstat | 3 files changed, 168 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.json Mon Apr 01 03:41:42 2024 +0000 @@ -0,0 +1,9 @@ +{ + "assemblies": [], + "configuration": {}, + "connections": [], + "defaultSession": { + "name": "New Session" + }, + "tracks": [] +} \ No newline at end of file
--- a/jbrowse2.py Sat Mar 30 22:33:29 2024 +0000 +++ b/jbrowse2.py Mon Apr 01 03:41:42 2024 +0000 @@ -8,6 +8,7 @@ import os import re import shutil +import ssl import struct import subprocess import tempfile @@ -39,6 +40,32 @@ } +INDEX_TEMPLATE = """<!doctype html> +<html lang="en" style="height:100%"> +<head> +<meta charset="utf-8"/> +<link rel="shortcut icon" href="./favicon.ico"/> +<meta name="viewport" content="width=device-width,initial-scale=1"/> +<meta name="theme-color" content="#000000"/> +<meta name="description" content="A fast and flexible genome browser"/> +<link rel="manifest" href="./manifest.json"/> +<title>JBrowse</title> +</script> +</head> +<body style="overscroll-behavior:none; height:100%; margin: 0;"> +<iframe + id="jbframe" + title="JBrowse2" + frameborder="0" + width="100%" + height="100%" + src='index_noview.html?config=config.json__SESSION_SPEC__'> +</iframe> +</body> +</html> +""" + + class ColorScaling(object): COLOR_FUNCTION_TEMPLATE = """ @@ -376,6 +403,9 @@ def __init__(self, outdir, jbrowse2path): self.assemblies = [] # these require more than a few line diff. self.assmeta = {} + self.ass_first_contigs = ( + [] + ) # for default session - these are read as first line of the assembly .fai self.giURL = GALAXY_INFRASTRUCTURE_URL self.outdir = outdir self.jbrowse2path = jbrowse2path @@ -451,6 +481,22 @@ } return wstyle + def urllib_get_2018(): + # Using a protected member like this is not any more fragile + # than extending the class and using it. I would use it. + url = "https://localhost:6667/my-endpoint" + ssl._create_default_https_context = ssl._create_unverified_context + with urllib.request.urlopen(url=url) as f: + print(f.read().decode("utf-8")) + + def urllib_get_2022(): + # Finally! Able to use the publice API. Happy happy! + url = "https://localhost:6667/my-endpoint" + scontext = ssl.SSLContext(ssl.PROTOCOL_TLS) + scontext.verify_mode = ssl.VerifyMode.CERT_NONE + with urllib.request.urlopen(url=url, context=scontext) as f: + print(f.read().decode("utf-8")) + def process_genomes(self, genomes): assembly = [] assmeta = [] @@ -469,8 +515,9 @@ fapath = genome_node["path"] if not useuri: fapath = os.path.realpath(fapath) - assem = self.make_assembly(fapath, genome_name, useuri) + assem, first_contig = self.make_assembly(fapath, genome_name, useuri) assembly.append(assem) + self.ass_first_contigs.append(first_contig) if len(genome_names) == 0: this_genome["genome_name"] = genome_name # first one for all tracks genome_names.append(genome_name) @@ -491,7 +538,12 @@ this_genome["genome_firstcontig"] = fl else: try: - fl = urllib.request.urlopen(fapath + ".fai").readline() + scontext = ssl.SSLContext(ssl.PROTOCOL_TLS) + scontext.verify_mode = ssl.VerifyMode.CERT_NONE + with urllib.request.urlopen( + url=fapath + ".fai", context=scontext + ) as f: + fl = f.readline() except Exception: fl = None if fl: # is first row of the text fai so the first contig name @@ -506,6 +558,9 @@ return this_genome["genome_name"] def make_assembly(self, fapath, gname, useuri): + """added code to grab the first contig name and length for broken default session from Anthony and Helena's code + that poor Bjoern is trying to figure out. + """ if useuri: faname = fapath adapter = { @@ -514,6 +569,12 @@ "faiLocation": {"uri": faname + ".fai", "locationType": "UriLocation"}, "gziLocation": {"uri": faname + ".gzi", "locationType": "UriLocation"}, } + scontext = ssl.SSLContext(ssl.PROTOCOL_TLS) + scontext.verify_mode = ssl.VerifyMode.CERT_NONE + with urllib.request.urlopen(url=faname + ".fai", context=scontext) as f: + fl = f.readline() + contig = fl.decode("utf8").strip() + # Merlin 172788 8 60 61 else: faname = gname + ".fa.gz" fadest = os.path.realpath(os.path.join(self.outdir, faname)) @@ -537,7 +598,9 @@ "uri": faname + ".gzi", }, } - + contig = open(fadest + ".fai", "r").readline().strip() + first_contig = contig.split()[:2] + first_contig.insert(0, gname) trackDict = { "name": gname, "sequence": { @@ -556,7 +619,7 @@ }, ], } - return trackDict + return (trackDict, first_contig) def add_default_view(self): cmd = [ @@ -907,7 +970,6 @@ else: cpath = os.path.realpath(dest) + ".crai" cmd = ["samtools", "index", "-c", "-o", cpath, os.path.realpath(dest)] - logging.debug("executing cmd %s" % " ".join(cmd)) self.subprocess_check_call(cmd) trackDict = { "type": "AlignmentsTrack", @@ -1136,7 +1198,7 @@ if gname not in self.genome_names: # ignore if already there - eg for duplicates among pafs. - asstrack = self.make_assembly(pgpaths[i], gname, useuri) + asstrack, first_contig = self.make_assembly(pgpaths[i], gname, useuri) self.genome_names.append(gname) self.tracksToAdd[gname] = [] self.assemblies.append(asstrack) @@ -1318,7 +1380,7 @@ for track_conf in self.tracksToAdd[gnome]: tId = track_conf["trackId"] track_types[tId] = track_conf["type"] - style_data = default_data["style"].get(tId, None) + style_data = default_data[gnome]["style"].get(tId, None) if not style_data: logging.debug( "### No style data in default data %s for %s" @@ -1393,6 +1455,75 @@ with open(self.config_json_file, "w") as config_file: json.dump(self.config_json, config_file, indent=2) + def add_defsess_to_index(self, data): + """ + Broken in Anthony's PR because only ever dealt with the first assembly. + + Add some default session settings: set some assemblies/tracks on/off + + This allows to select a default view: + - jb type (Linear, Circular, etc) + - default location on an assembly + - default tracks + - ... + + Different methods to do that were tested/discussed: + - using a defaultSession item in config.json: this proved to be difficult: + forced to write a full session block, including hard-coded/hard-to-guess items, + no good way to let Jbrowse2 display a scaffold without knowing its size + - using JBrowse2 as an embedded React component in a tool-generated html file: + it works but it requires generating js code to actually do what we want = chosing default view, assembly, tracks, ... + - writing a session-spec inside the config.json file: this is not yet supported as of 2.10.2 (see PR 4148 below) + a session-spec is a kind of simplified defaultSession where you don't need to specify every aspect of the session + - passing a session-spec through URL params by embedding the JBrowse2 index.html inside an iframe + we selected this option + + Xrefs to understand the choices: + https://github.com/GMOD/jbrowse-components/issues/2708 + https://github.com/GMOD/jbrowse-components/discussions/3568 + https://github.com/GMOD/jbrowse-components/pull/4148 + """ + new_index = "Nothing written" + session_spec = {"views": []} + logging.debug("def data=%s" % data) + for first_contig in self.ass_first_contigs: + gnome, refName, end = first_contig + start = 0 + if False or data.get("defaultLocation", ""): + loc_match = re.search( + r"^([^:]+):([\d,]*)\.*([\d,]*)$", data["defaultLocation"] + ) + # loc_match = re.search(r"^(\w+):(\d+)\.+(\d+)$", data["defaultLocation"]) + if loc_match: + refName = loc_match.group(1) + start = int(loc_match.group(2)) + end = int(loc_match.group(3)) + else: + if refName: + view = { + "assembly": gnome, + "loc": "{}:{}-{}".format(refName, start, end), + "type": "LinearGenomeView", + "tracks": data[gnome]["tracks"], + } + session_spec["views"].append(view) + sess = json.dumps(session_spec, sort_keys=True, indent=2) + new_index = INDEX_TEMPLATE.replace( + "__SESSION_SPEC__", "&session=spec-{}".format(sess) + ) + + os.rename( + os.path.join(self.outdir, "index.html"), + os.path.join(self.outdir, "index_noview.html"), + ) + + with open(os.path.join(self.outdir, "index.html"), "w") as nind: + nind.write(new_index) + logging.debug( + "#### add_defsession gnome=%s refname=%s\nsession_spec=%s\nnew_index=%s" + % (gnome, refName, sess, new_index) + ) + def add_general_configuration(self, data): """ Add some general configuration to the config.json file @@ -1426,7 +1557,7 @@ with open(config_path, "w") as config_file: json.dump(self.config_json, config_file, indent=2) - def clone_jbrowse(self, realclone=True): + def clone_jbrowse(self, realclone=False): """Clone a JBrowse directory into a destination directory. This also works in Biocontainer testing now Leave as True between version updates on temporary tools - requires manual conda trigger :( """ @@ -1480,14 +1611,7 @@ jc = JbrowseConnector(outdir=args.outdir, jbrowse2path=args.jbrowse2path) - default_session_data = { - "visibility": { - "default_on": [], - "default_off": [], - }, - "style": {}, - "style_labels": {}, - } + default_session_data = {} for ass in root.findall("assembly"): genomes = [ @@ -1500,7 +1624,16 @@ for x in ass.findall("metadata/genomes/genome") ] assref_name = jc.process_genomes(genomes) - + if not default_session_data.get(assref_name, None): + default_session_data[assref_name] = { + "tracks": [], + "style": {}, + "style_labels": {}, + "visibility": { + "default_on": [], + "default_off": [], + }, + } for track in ass.find("tracks"): track_conf = {} track_conf["trackfiles"] = [] @@ -1577,21 +1710,22 @@ vis = track.attrib.get("visibility", "default_off") if not vis: vis = "default_off" - default_session_data["visibility"][vis].append(key) + default_session_data[assref_name]["visibility"][vis].append(key) if track.find("options/style"): - default_session_data["style"][key] = { + default_session_data[assref_name]["style"][key] = { item.tag: parse_style_conf(item) for item in track.find("options/style") } else: - default_session_data["style"][key] = {} - logging.debug("@@@@ no options/style found for %s" % (key)) + default_session_data[assref_name]["style"][key] = {} + logging.debug("no options/style found for %s" % (key)) if track.find("options/style_labels"): - default_session_data["style_labels"][key] = { + default_session_data[assref_name]["style_labels"][key] = { item.tag: parse_style_conf(item) for item in track.find("options/style_labels") } + default_session_data[assref_name]["tracks"].append(key) default_session_data["defaultLocation"] = root.find( "metadata/general/defaultLocation" ).text @@ -1616,7 +1750,8 @@ assconf = jc.config_json.get("assemblies", []) assconf += jc.assemblies jc.config_json["assemblies"] = assconf - logging.debug("&&&assemblies=%s, gnames=%s" % (assconf, jc.genome_names)) + logging.debug("assemblies=%s, gnames=%s" % (assconf, jc.genome_names)) jc.write_config() jc.add_default_session(default_session_data) + jc.add_defsess_to_index(default_session_data) # jc.text_index() not sure what broke here.
--- a/jbrowse2.xml Sat Mar 30 22:33:29 2024 +0000 +++ b/jbrowse2.xml Mon Apr 01 03:41:42 2024 +0000 @@ -1,4 +1,4 @@ - <tool id="jbrowse2" name="JBrowse2" version="@TOOL_VERSION@+@WRAPPER_VERSION@_8" profile="22.05"> + <tool id="jbrowse2" name="JBrowse2" version="@TOOL_VERSION@+@WRAPPER_VERSION@_10" profile="22.05"> <description>genome browser</description> <macros> <import>macros.xml</import>