comparison jbrowse2.py @ 97:74074746ccd8 draft

planemo upload for repository https://github.com/usegalaxy-eu/temporary-tools/tree/master/jbrowse2 commit 1c04ea76330d0148a7682b3c26846e5a5df21f99
author fubar
date Sat, 01 Jun 2024 05:37:13 +0000
parents 4c517a0041a8
children b1260bca5fdc
comparison
equal deleted inserted replaced
96:5ef1ba2031f2 97:74074746ccd8
20 log = logging.getLogger("jbrowse") 20 log = logging.getLogger("jbrowse")
21 21
22 JB2VER = "v2.11.0" 22 JB2VER = "v2.11.0"
23 # version pinned if cloning - but not cloning now 23 # version pinned if cloning - but not cloning now
24 logCommands = True 24 logCommands = True
25 # useful for seeing what's being written but not for production setups 25 # useful for seeing what's being written but NOT for production setups
26 TODAY = datetime.datetime.now().strftime("%Y-%m-%d") 26 TODAY = datetime.datetime.now().strftime("%Y-%m-%d")
27 SELF_LOCATION = os.path.dirname(os.path.realpath(__file__)) 27 SELF_LOCATION = os.path.dirname(os.path.realpath(__file__))
28 GALAXY_INFRASTRUCTURE_URL = None 28 GALAXY_INFRASTRUCTURE_URL = None
29 mapped_chars = { 29 mapped_chars = {
30 ">": "__gt__", 30 ">": "__gt__",
37 "}": "__cc__", 37 "}": "__cc__",
38 "@": "__at__", 38 "@": "__at__",
39 "#": "__pd__", 39 "#": "__pd__",
40 "": "__cn__", 40 "": "__cn__",
41 } 41 }
42
43
44 INDEX_TEMPLATE = """<!doctype html>
45 <html lang="en" style="height:100%">
46 <head>
47 <meta charset="utf-8"/>
48 <link rel="shortcut icon" href="./favicon.ico"/>
49 <meta name="viewport" content="width=device-width,initial-scale=1"/>
50 <meta name="theme-color" content="#000000"/>
51 <meta name="description" content="A fast and flexible genome browser"/>
52 <link rel="manifest" href="./manifest.json"/>
53 <title>JBrowse</title>
54 </script>
55 </head>
56 <body style="overscroll-behavior:none; height:100%; margin: 0;">
57 <iframe
58 id="jbframe"
59 title="JBrowse2"
60 frameborder="0"
61 width="100%"
62 height="100%"
63 src='index_noview.html?config=config.json__SESSION_SPEC__'>
64 </iframe>
65 </body>
66 </html>
67 """
68 42
69 43
70 class ColorScaling(object): 44 class ColorScaling(object):
71 45
72 COLOR_FUNCTION_TEMPLATE = """ 46 COLOR_FUNCTION_TEMPLATE = """
489 if not useuri: 463 if not useuri:
490 fl = open(url, "r").readlines() 464 fl = open(url, "r").readlines()
491 nrow = len(fl) 465 nrow = len(fl)
492 else: 466 else:
493 try: 467 try:
494 scontext = ssl.SSLContext(ssl.PROTOCOL_TLS) 468 scontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
469 scontext.check_hostname = False
495 scontext.verify_mode = ssl.VerifyMode.CERT_NONE 470 scontext.verify_mode = ssl.VerifyMode.CERT_NONE
496 with urllib.request.urlopen(url, context=scontext) as f: 471 with urllib.request.urlopen(url, context=scontext) as f:
497 fl = f.readlines() 472 fl = f.readlines()
498 nrow = len(fl) 473 nrow = len(fl)
499 except Exception: 474 except Exception:
542 """added code to grab the first contig name and length for broken default session from Anthony and Helena's code 517 """added code to grab the first contig name and length for broken default session from Anthony and Helena's code
543 that poor Bjoern is trying to figure out. 518 that poor Bjoern is trying to figure out.
544 """ 519 """
545 if useuri: 520 if useuri:
546 faname = fapath 521 faname = fapath
547 scontext = ssl.SSLContext(ssl.PROTOCOL_TLS) 522 scontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
523 scontext.check_hostname = False
548 scontext.verify_mode = ssl.VerifyMode.CERT_NONE 524 scontext.verify_mode = ssl.VerifyMode.CERT_NONE
549 with urllib.request.urlopen(url=faname + ".fai", context=scontext) as f: 525 with urllib.request.urlopen(url=faname + ".fai", context=scontext) as f:
550 fl = f.readline() 526 fl = f.readline()
551 contig = fl.decode("utf8").strip() 527 contig = fl.decode("utf8").strip()
552 # Merlin 172788 8 60 61 528 # Merlin 172788 8 60 61
895 gsa = self.assmeta.get(trackData["assemblyNames"], None) 871 gsa = self.assmeta.get(trackData["assemblyNames"], None)
896 if gsa: 872 if gsa:
897 genseqad = gsa[0]["genome_sequence_adapter"] 873 genseqad = gsa[0]["genome_sequence_adapter"]
898 else: 874 else:
899 genseqad = "Not found" 875 genseqad = "Not found"
900 logging.warn("No adapter found for cram %s in gsa=%s" % (tId, gsa)) 876 logging.warning("No adapter found for cram %s in gsa=%s" % (tId, gsa))
901 if useuri: 877 if useuri:
902 url = data 878 url = data
903 else: 879 else:
904 fname = tId 880 fname = tId
905 dest = os.path.join(self.outdir, fname) 881 dest = os.path.join(self.outdir, fname)
963 useuri = trackData["useuri"].lower() == "yes" 939 useuri = trackData["useuri"].lower() == "yes"
964 if useuri: 940 if useuri:
965 url = data 941 url = data
966 else: 942 else:
967 url = tId 943 url = tId
968 dest = "%s/%s" % (self.outdir, url) 944 dest = os.path.join(self.outdir, url)
969 cmd = "bgzip -c %s > %s" % (data, dest) 945 cmd = "bgzip -c %s > %s" % (data, dest)
970 self.subprocess_popen(cmd) 946 self.subprocess_popen(cmd)
971 cmd = ["tabix", "-f", "-p", "vcf", dest] 947 cmd = ["tabix", "-f", "-p", "vcf", dest]
972 self.subprocess_check_call(cmd) 948 self.subprocess_check_call(cmd)
973 trackDict = { 949 trackDict = {
1030 useuri = trackData["useuri"].lower() == "yes" 1006 useuri = trackData["useuri"].lower() == "yes"
1031 if useuri: 1007 if useuri:
1032 url = trackData["path"] 1008 url = trackData["path"]
1033 else: 1009 else:
1034 url = tId + ".gz" 1010 url = tId + ".gz"
1035 dest = "%s/%s" % (self.outdir, url) 1011 dest = os.path.join(self.outdir, url)
1036 self._sort_gff(data, dest) 1012 self._sort_gff(data, dest)
1037 categ = trackData["category"] 1013 categ = trackData["category"]
1038 trackDict = { 1014 trackDict = {
1039 "type": "FeatureTrack", 1015 "type": "FeatureTrack",
1040 "trackId": tId, 1016 "trackId": tId,
1076 useuri = trackData["useuri"].lower() == "yes" 1052 useuri = trackData["useuri"].lower() == "yes"
1077 if useuri: 1053 if useuri:
1078 url = data 1054 url = data
1079 else: 1055 else:
1080 url = tId + ".gz" 1056 url = tId + ".gz"
1081 dest = "%s/%s" % (self.outdir, url) 1057 dest = os.path.join(self.outdir, url)
1082 self._sort_bed(data, dest) 1058 self._sort_bed(data, dest)
1083 trackDict = { 1059 trackDict = {
1084 "type": "FeatureTrack", 1060 "type": "FeatureTrack",
1085 "trackId": tId, 1061 "trackId": tId,
1086 "name": trackData["name"], 1062 "name": trackData["name"],
1123 tname = trackData["name"] 1099 tname = trackData["name"]
1124 tId = trackData["label"] 1100 tId = trackData["label"]
1125 url = tId 1101 url = tId
1126 useuri = data.startswith("http://") or data.startswith("https://") 1102 useuri = data.startswith("http://") or data.startswith("https://")
1127 if not useuri: 1103 if not useuri:
1128 dest = "%s/%s" % (self.outdir, url) 1104 dest = os.path.join(self.outdir, url)
1129 self.symlink_or_copy(os.path.realpath(data), dest) 1105 self.symlink_or_copy(os.path.realpath(data), dest)
1130 nrow = self.getNrow(dest) 1106 nrow = self.getNrow(dest)
1131 else: 1107 else:
1132 url = data 1108 url = data
1133 nrow = self.getNrow(url) 1109 nrow = self.getNrow(url)
1325 default session settings are hard and fragile. 1301 default session settings are hard and fragile.
1326 .add_default_view() and other configuration code adapted from 1302 .add_default_view() and other configuration code adapted from
1327 https://github.com/abretaud/tools-iuc/blob/jbrowse2/tools/jbrowse2/jbrowse2.py 1303 https://github.com/abretaud/tools-iuc/blob/jbrowse2/tools/jbrowse2/jbrowse2.py
1328 """ 1304 """
1329 # TODO using the default session for now, but check out session specs in the future https://github.com/GMOD/jbrowse-components/issues/2708 1305 # TODO using the default session for now, but check out session specs in the future https://github.com/GMOD/jbrowse-components/issues/2708
1306 bpPerPx = 50 # this is tricky since browser window width is unknown - this seems a compromise that sort of works....
1330 track_types = {} 1307 track_types = {}
1331 with open(self.config_json_file, "r") as config_file: 1308 with open(self.config_json_file, "r") as config_file:
1332 config_json = json.load(config_file) 1309 config_json = json.load(config_file)
1333 if self.config_json: 1310 if self.config_json:
1334 config_json.update(self.config_json) 1311 config_json.update(self.config_json)
1363 "type": track_types[tId], 1340 "type": track_types[tId],
1364 "configuration": tId, 1341 "configuration": tId,
1365 "displays": [style_data], 1342 "displays": [style_data],
1366 } 1343 }
1367 ) 1344 )
1368 view_json = {
1369 "type": "LinearGenomeView",
1370 "offsetPx": 0,
1371 "minimized": False,
1372 "tracks": tracks_data,
1373 }
1374 first = [x for x in self.ass_first_contigs if x[0] == gnome] 1345 first = [x for x in self.ass_first_contigs if x[0] == gnome]
1375 if len(first) > 0: 1346 drdict = {
1376 [gnome, refName, end] = first[0]
1377 start = 0
1378 end = int(end)
1379 drdict = {
1380 "refName": refName,
1381 "start": start,
1382 "end": end,
1383 "reversed": False, 1347 "reversed": False,
1384 "assemblyName": gnome, 1348 "assemblyName": gnome,
1385 } 1349 }
1350 if len(first) > 0:
1351 [gnome, refName, end] = first[0]
1352 drdict["refName"] = refName
1353 drdict["start"] = 0
1354 end = int(end)
1355 drdict["end"] = end
1386 else: 1356 else:
1387 ddl = default_data.get("defaultLocation", None) 1357 ddl = default_data.get("defaultLocation", None)
1388 if ddl: 1358 if ddl:
1389 loc_match = re.search(r"^([^:]+):([\d,]*)\.*([\d,]*)$", ddl) 1359 loc_match = re.search(r"^([^:]+):([\d,]*)\.*([\d,]*)$", ddl)
1390 # allow commas like 100,000 but ignore as integer 1360 # allow commas like 100,000 but ignore as integer
1398 else: 1368 else:
1399 logging.info( 1369 logging.info(
1400 "@@@ regexp could not match contig:start..end in the supplied location %s - please fix" 1370 "@@@ regexp could not match contig:start..end in the supplied location %s - please fix"
1401 % ddl 1371 % ddl
1402 ) 1372 )
1373 view_json = {
1374 "type": "LinearGenomeView",
1375 "offsetPx": 0,
1376 "bpPerPx" : bpPerPx,
1377 "minimized": False,
1378 "tracks": tracks_data
1379 }
1403 if drdict.get("refName", None): 1380 if drdict.get("refName", None):
1404 # TODO displayedRegions is not just zooming to the region, it hides the rest of the chromosome 1381 # TODO displayedRegions is not just zooming to the region, it hides the rest of the chromosome
1405 view_json["displayedRegions"] = [ 1382 view_json["displayedRegions"] = [
1406 drdict, 1383 drdict,
1407 ] 1384 ]
1413 session_views.append(view_json) 1390 session_views.append(view_json)
1414 session_name = default_data.get("session_name", "New session") 1391 session_name = default_data.get("session_name", "New session")
1415 for key, value in mapped_chars.items(): 1392 for key, value in mapped_chars.items():
1416 session_name = session_name.replace(value, key) 1393 session_name = session_name.replace(value, key)
1417 session_json["name"] = session_name 1394 session_json["name"] = session_name
1418
1419 if "views" not in session_json: 1395 if "views" not in session_json:
1420 session_json["views"] = session_views 1396 session_json["views"] = session_views
1421 else: 1397 else:
1422 session_json["views"] += session_views 1398 session_json["views"] += session_views
1423 pp = json.dumps(session_views, indent=2) 1399 pp = json.dumps(session_views, indent=2)
1427 with open(self.config_json_file, "w") as config_file: 1403 with open(self.config_json_file, "w") as config_file:
1428 json.dump(self.config_json, config_file, indent=2) 1404 json.dump(self.config_json, config_file, indent=2)
1429 1405
1430 def add_defsess_to_index(self, data): 1406 def add_defsess_to_index(self, data):
1431 """ 1407 """
1432 PROBABLY NOW BROKEN by changes since this was deprecated temporarily as at April 18 1408 ----------------------------------------------------------
1433
1434 Included on request of the new codeowner, from Anthony's IUC PR.
1435 Had to be fixed to keep each assembly with the associated tracks for a default view.
1436 Originally used only the first assembly, putting all tracks there and so breaking some
1437 when tested with 2 or more.
1438
1439 ----------------------------------------------------------
1440 Add some default session settings: set some assemblies/tracks on/off 1409 Add some default session settings: set some assemblies/tracks on/off
1441 1410
1442 This allows to select a default view: 1411 This allows to select a default view:
1443 - jb type (Linear, Circular, etc) 1412 - jb type (Linear, Circular, etc)
1444 - default location on an assembly 1413 - default location on an assembly
1459 Xrefs to understand the choices: 1428 Xrefs to understand the choices:
1460 https://github.com/GMOD/jbrowse-components/issues/2708 1429 https://github.com/GMOD/jbrowse-components/issues/2708
1461 https://github.com/GMOD/jbrowse-components/discussions/3568 1430 https://github.com/GMOD/jbrowse-components/discussions/3568
1462 https://github.com/GMOD/jbrowse-components/pull/4148 1431 https://github.com/GMOD/jbrowse-components/pull/4148
1463 """ 1432 """
1433
1434
1435 INDEX_TEMPLATE = """<!doctype html>
1436 <html lang="en" style="height:100%">
1437 <head>
1438 <meta charset="utf-8"/>
1439 <link rel="shortcut icon" href="./favicon.ico"/>
1440 <meta name="viewport" content="width=device-width,initial-scale=1"/>
1441 <meta name="theme-color" content="#000000"/>
1442 <meta name="description" content="A fast and flexible genome browser"/>
1443 <link rel="manifest" href="./manifest.json"/>
1444 <title>JBrowse</title>
1445 </script>
1446 </head>
1447 <body style="overscroll-behavior:none; height:100%; margin: 0;">
1448 <iframe
1449 id="jbframe"
1450 title="JBrowse2"
1451 frameborder="0"
1452 width="100%"
1453 height="100%"
1454 src='index_noview.html?config=config.json__SESSION_SPEC__'>
1455 </iframe>
1456 </body>
1457 </html>
1458 """
1459
1464 new_index = "Nothing written" 1460 new_index = "Nothing written"
1465 session_spec = {"views": []} 1461 session_spec = {"views": []}
1466 logging.debug("def ass_first=%s\ndata=%s" % (self.ass_first_contigs, data)) 1462 logging.debug("def ass_first=%s\ndata=%s" % (self.ass_first_contigs, data))
1467 for first_contig in self.ass_first_contigs: 1463 for first_contig in self.ass_first_contigs:
1468 logging.debug("first contig=%s" % self.ass_first_contigs) 1464 logging.debug("first contig=%s" % self.ass_first_contigs)
1542 "robots.txt", 1538 "robots.txt",
1543 "umd_plugin.js", 1539 "umd_plugin.js",
1544 "version.txt", 1540 "version.txt",
1545 "test_data", 1541 "test_data",
1546 ]: 1542 ]:
1547 cmd = ["rm", "-rf", os.path.join(dest, fn)] 1543 try:
1548 self.subprocess_check_call(cmd) 1544 path = os.path.join(dest, fn)
1549 cmd = ["cp", os.path.join(INSTALLED_TO, "jb2_webserver.py"), dest] 1545 if os.path.isdir(path):
1550 self.subprocess_check_call(cmd) 1546 shutil.rmtree(path)
1547 else:
1548 os.remove(path)
1549 except OSError as e:
1550 log.error("Error: %s - %s." % (e.filename, e.strerror))
1551 shutil.copyfile(os.path.join(INSTALLED_TO, "jb2_webserver.py"), os.path.join(dest, "jb2_webserver.py"))
1551 1552
1552 1553
1553 def parse_style_conf(item): 1554 def parse_style_conf(item):
1554 if item.text.lower() in ["false", "true", "yes", "no"]: 1555 if item.text.lower() in ["false", "true", "yes", "no"]:
1555 return item.text.lower in ("yes", "true") 1556 return item.text.lower in ("yes", "true")
1559 1560
1560 if __name__ == "__main__": 1561 if __name__ == "__main__":
1561 parser = argparse.ArgumentParser(description="", epilog="") 1562 parser = argparse.ArgumentParser(description="", epilog="")
1562 parser.add_argument("--xml", help="Track Configuration") 1563 parser.add_argument("--xml", help="Track Configuration")
1563 parser.add_argument( 1564 parser.add_argument(
1564 "--jbrowse2path", help="Path to JBrowse2 directory in biocontainer or Conda" 1565 "--jbrowse2path", help="Path to JBrowse2 directory in BioContainer or Conda"
1565 ) 1566 )
1566 parser.add_argument("--outdir", help="Output directory", default="out") 1567 parser.add_argument("--outdir", help="Output directory", default="out")
1567 parser.add_argument("--version", "-V", action="version", version=JB2VER) 1568 parser.add_argument("--version", "-V", action="version", version=JB2VER)
1568 args = parser.parse_args() 1569 args = parser.parse_args()
1569 tree = ET.parse(args.xml) 1570 tree = ET.parse(args.xml)