Mercurial > repos > fubar > toolfactory_gtn
comparison toolfactory/ToolFactory.py @ 5:e2c8c2fa192d draft
Uploaded
| author | fubar |
|---|---|
| date | Tue, 27 Apr 2021 23:33:49 +0000 |
| parents | 2a46da701dde |
| children | efefe43f23c8 |
comparison
equal
deleted
inserted
replaced
| 4:2a46da701dde | 5:e2c8c2fa192d |
|---|---|
| 68 citation_tuples.append(("doi", citation[len("doi") :].strip())) | 68 citation_tuples.append(("doi", citation[len("doi") :].strip())) |
| 69 else: | 69 else: |
| 70 citation_tuples.append(("bibtex", citation[len("bibtex") :].strip())) | 70 citation_tuples.append(("bibtex", citation[len("bibtex") :].strip())) |
| 71 return citation_tuples | 71 return citation_tuples |
| 72 | 72 |
| 73 class ToolTester(): | |
| 74 # requires highly insecure docker settings - like write to tool_conf.xml and to tools ! | |
| 75 # if in a container possibly not so courageous. | |
| 76 # Fine on your own laptop but security red flag for most production instances | |
| 77 # uncompress passed tar, run planemo and rebuild a new tarball with tests | |
| 78 | |
| 79 def __init__(self, report_dir, in_tool_archive, new_tool_archive, include_tests, galaxy_root): | |
| 80 self.new_tool_archive = new_tool_archive | |
| 81 self.include_tests = include_tests | |
| 82 self.galaxy_root = galaxy_root | |
| 83 self.repdir = report_dir | |
| 84 assert in_tool_archive and tarfile.is_tarfile(in_tool_archive) | |
| 85 # this is not going to go well with arbitrary names. TODO introspect tool xml! | |
| 86 tff = tarfile.open(in_tool_archive, "r:*") | |
| 87 flist = tff.getnames() | |
| 88 ourdir = os.path.commonpath(flist) # eg pyrevpos | |
| 89 self.tool_name = ourdir | |
| 90 ourxmls = [x for x in flist if x.lower().endswith('.xml') and os.path.split(x)[0] == ourdir] | |
| 91 # planemo_test/planemo_test.xml | |
| 92 assert len(ourxmls) > 0 | |
| 93 self.ourxmls = ourxmls # [os.path.join(tool_path,x) for x in ourxmls] | |
| 94 res = tff.extractall() | |
| 95 tff.close() | |
| 96 self.update_tests(ourdir) | |
| 97 self.tooloutdir = ourdir | |
| 98 self.testdir = os.path.join(self.tooloutdir, "test-data") | |
| 99 if not os.path.exists(self.tooloutdir): | |
| 100 os.mkdir(self.tooloutdir) | |
| 101 if not os.path.exists(self.testdir): | |
| 102 os.mkdir(self.testdir) | |
| 103 if not os.path.exists(self.repdir): | |
| 104 os.mkdir(self.repdir) | |
| 105 if not os.path.exists(self.tooloutdir): | |
| 106 os.mkdir(self.tooloutdir) | |
| 107 if not os.path.exists(self.testdir): | |
| 108 os.mkdir(self.testdir) | |
| 109 if not os.path.exists(self.repdir): | |
| 110 os.mkdir(self.repdir) | |
| 111 self.moveRunOutputs() | |
| 112 self.makeToolTar() | |
| 113 | |
| 114 def call_planemo(self,xmlpath,ourdir): | |
| 115 penv = os.environ | |
| 116 penv['HOME'] = os.path.join(self.galaxy_root,'planemo') | |
| 117 #penv["GALAXY_VIRTUAL_ENV"] = os.path.join(penv['HOME'],'.planemo','gx_venv_3.9') | |
| 118 penv["PIP_CACHE_DIR"] = os.path.join(self.galaxy_root,'pipcache') | |
| 119 toolfile = os.path.split(xmlpath)[1] | |
| 120 tool_name = self.tool_name | |
| 121 tool_test_output = os.path.join(self.repdir, f"{tool_name}_planemo_test_report.html") | |
| 122 cll = ["planemo", | |
| 123 "test", | |
| 124 #"--job_config_file", | |
| 125 # os.path.join(self.galaxy_root,"config","job_conf.xml"), | |
| 126 #"--galaxy_python_version", | |
| 127 #"3.9", | |
| 128 "--test_output", | |
| 129 os.path.abspath(tool_test_output), | |
| 130 "--galaxy_root", | |
| 131 self.galaxy_root, | |
| 132 "--update_test_data", | |
| 133 os.path.abspath(xmlpath), | |
| 134 ] | |
| 135 print("Call planemo cl =", cll) | |
| 136 p = subprocess.run( | |
| 137 cll, | |
| 138 capture_output=True, | |
| 139 encoding='utf8', | |
| 140 env = penv, | |
| 141 shell=False, | |
| 142 ) | |
| 143 return p | |
| 144 | |
| 145 def makeToolTar(self): | |
| 146 """move outputs into test-data and prepare the tarball""" | |
| 147 excludeme = "_planemo_test_report.html" | |
| 148 | |
| 149 def exclude_function(tarinfo): | |
| 150 filename = tarinfo.name | |
| 151 return None if filename.endswith(excludeme) else tarinfo | |
| 152 | |
| 153 newtar = 'new_%s_toolshed.gz' % self.tool_name | |
| 154 ttf = tarfile.open(newtar, "w:gz") | |
| 155 ttf.add(name=self.tooloutdir, | |
| 156 arcname=self.tool_name, | |
| 157 filter=exclude_function) | |
| 158 ttf.close() | |
| 159 shutil.copyfile(newtar, self.new_tool_archive) | |
| 160 | |
| 161 def move_One(self,scandir): | |
| 162 with os.scandir('.') as outs: | |
| 163 for entry in outs: | |
| 164 newname = entry.name | |
| 165 if not entry.is_file() or entry.name.endswith('_sample'): | |
| 166 continue | |
| 167 if not (entry.name.endswith('.html') or entry.name.endswith('.gz') or entry.name.endswith(".tgz")): | |
| 168 fname, ext = os.path.splitext(entry.name) | |
| 169 if len(ext) > 1: | |
| 170 newname = f"{fname}_{ext[1:]}.txt" | |
| 171 else: | |
| 172 newname = f"{fname}.txt" | |
| 173 dest = os.path.join(self.repdir, newname) | |
| 174 src = entry.name | |
| 175 shutil.copyfile(src, dest) | |
| 176 | |
| 177 def moveRunOutputs(self): | |
| 178 """need to move planemo or run outputs into toolfactory collection""" | |
| 179 self.move_One(self.tooloutdir) | |
| 180 self.move_One('.') | |
| 181 if self.include_tests: | |
| 182 self.move_One(self.testdir) | |
| 183 | |
| 184 def update_tests(self,ourdir): | |
| 185 for xmlf in self.ourxmls: | |
| 186 capture = self.call_planemo(xmlf,ourdir) | |
| 187 logf = open(f"%s_run_report" % (self.tool_name),'w') | |
| 188 logf.write("stdout:") | |
| 189 logf.write(capture.stdout) | |
| 190 logf.write("stderr:") | |
| 191 logf.write(capture.stderr) | |
| 192 | |
| 193 | |
| 194 class ToolConfUpdater(): | 73 class ToolConfUpdater(): |
| 195 # update config/tool_conf.xml with a new tool unpacked in /tools | 74 # update config/tool_conf.xml with a new tool unpacked in /tools |
| 196 # requires highly insecure docker settings - like write to tool_conf.xml and to tools ! | 75 # requires highly insecure docker settings - like write to tool_conf.xml and to tools ! |
| 197 # if in a container possibly not so courageous. | 76 # if in a container possibly not so courageous. |
| 198 # Fine on your own laptop but security red flag for most production instances | 77 # Fine on your own laptop but security red flag for most production instances |
| 199 | 78 |
| 200 def __init__(self, args, tool_conf_path, new_tool_archive_path, new_tool_name, tool_dir): | 79 def __init__(self, args, tool_conf_path, new_tool_archive_path, new_tool_name, tool_dir): |
| 201 self.args = args | 80 self.args = args |
| 202 self.tool_conf_path = tool_conf_path | 81 self.tool_conf_path = os.path.join(args.galaxy_root,tool_conf_path) |
| 82 self.tool_dir = os.path.join(args.galaxy_root, tool_dir) | |
| 203 self.our_name = 'ToolFactory' | 83 self.our_name = 'ToolFactory' |
| 204 tff = tarfile.open(new_tool_archive_path, "r:*") | 84 tff = tarfile.open(new_tool_archive_path, "r:*") |
| 205 flist = tff.getnames() | 85 flist = tff.getnames() |
| 206 ourdir = os.path.commonpath(flist) # eg pyrevpos | 86 ourdir = os.path.commonpath(flist) # eg pyrevpos |
| 207 self.tool_id = ourdir # they are the same for TF tools | 87 self.tool_id = ourdir # they are the same for TF tools |
| 208 ourxml = [x for x in flist if x.lower().endswith('.xml')] | 88 ourxml = [x for x in flist if x.lower().endswith('.xml')] |
| 209 res = tff.extractall(tool_dir) | 89 res = tff.extractall() |
| 210 tff.close() | 90 tff.close() |
| 91 self.run_rsync(ourdir, self.tool_dir) | |
| 211 self.update_toolconf(ourdir,ourxml) | 92 self.update_toolconf(ourdir,ourxml) |
| 93 | |
| 94 def run_rsync(self, srcf, dstf): | |
| 95 src = os.path.abspath(srcf) | |
| 96 dst = os.path.abspath(dstf) | |
| 97 if os.path.isdir(src): | |
| 98 cll = ['rsync', '-vr', src, dst] | |
| 99 else: | |
| 100 cll = ['rsync', '-v', src, dst] | |
| 101 p = subprocess.run( | |
| 102 cll, | |
| 103 capture_output=False, | |
| 104 encoding='utf8', | |
| 105 shell=False, | |
| 106 ) | |
| 212 | 107 |
| 213 def install_deps(self): | 108 def install_deps(self): |
| 214 gi = galaxy.GalaxyInstance(url=self.args.galaxy_url, key=self.args.galaxy_api_key) | 109 gi = galaxy.GalaxyInstance(url=self.args.galaxy_url, key=self.args.galaxy_api_key) |
| 215 x = gi.tools.install_dependencies(self.tool_id) | 110 x = gi.tools.install_dependencies(self.tool_id) |
| 216 print(f"Called install_dependencies on {self.tool_id} - got {x}") | 111 print(f"Called install_dependencies on {self.tool_id} - got {x}") |
| 217 | 112 |
| 218 def update_toolconf(self,ourdir,ourxml): # path is relative to tools | 113 def update_toolconf(self,ourdir,ourxml): # path is relative to tools |
| 219 updated = False | 114 updated = False |
| 220 tree = ET.parse(self.tool_conf_path) | 115 localconf = './local_tool_conf.xml' |
| 116 self.run_rsync(self.tool_conf_path,localconf) | |
| 117 tree = ET.parse(localconf) | |
| 221 root = tree.getroot() | 118 root = tree.getroot() |
| 222 hasTF = False | 119 hasTF = False |
| 223 TFsection = None | 120 TFsection = None |
| 224 for e in root.findall('section'): | 121 for e in root.findall('section'): |
| 225 if e.attrib['name'] == self.our_name: | 122 if e.attrib['name'] == self.our_name: |
| 233 for xml in ourxml: # may be > 1 | 130 for xml in ourxml: # may be > 1 |
| 234 if not xml in conf_tools: # new | 131 if not xml in conf_tools: # new |
| 235 updated = True | 132 updated = True |
| 236 ET.SubElement(TFsection, 'tool', {'file':xml}) | 133 ET.SubElement(TFsection, 'tool', {'file':xml}) |
| 237 ET.indent(tree) | 134 ET.indent(tree) |
| 238 tree.write(self.tool_conf_path, pretty_print=True) | 135 newconf = f"{self.tool_id}_conf" |
| 136 tree.write(newconf, pretty_print=True) | |
| 137 self.run_rsync(newconf,self.tool_conf_path) | |
| 239 if False and self.args.packages and self.args.packages > '': | 138 if False and self.args.packages and self.args.packages > '': |
| 240 self.install_deps() | 139 self.install_deps() |
| 241 | 140 |
| 242 class ScriptRunner: | 141 class ScriptRunner: |
| 243 """Wrapper for an arbitrary script | 142 """Wrapper for an arbitrary script |
| 1169 a("--galaxy_venv", default="/galaxy_venv") | 1068 a("--galaxy_venv", default="/galaxy_venv") |
| 1170 a("--collection", action="append", default=[]) | 1069 a("--collection", action="append", default=[]) |
| 1171 a("--include_tests", default=False, action="store_true") | 1070 a("--include_tests", default=False, action="store_true") |
| 1172 a("--install", default=False, action="store_true") | 1071 a("--install", default=False, action="store_true") |
| 1173 a("--run_test", default=False, action="store_true") | 1072 a("--run_test", default=False, action="store_true") |
| 1174 a("--local_tools", default='tools') # relative to galaxy_root | 1073 a("--local_tools", default='tools') # relative to $__root_dir__ |
| 1175 a("--tool_conf_path", default='/galaxy_root/config/tool_conf.xml') | 1074 a("--tool_conf_path", default='config/tool_conf.xml') # relative to $__root_dir__ |
| 1176 a("--galaxy_url", default="http://localhost:8080") | 1075 a("--galaxy_url", default="http://localhost:8080") |
| 1177 a("--toolshed_url", default="http://localhost:9009") | 1076 a("--toolshed_url", default="http://localhost:9009") |
| 1178 # make sure this is identical to tool_sheds_conf.xml | 1077 # make sure this is identical to tool_sheds_conf.xml |
| 1179 # localhost != 127.0.0.1 so validation fails | 1078 # localhost != 127.0.0.1 so validation fails |
| 1180 a("--toolshed_api_key", default="fakekey") | 1079 a("--toolshed_api_key", default="fakekey") |
| 1201 r.makeToolTar() | 1100 r.makeToolTar() |
| 1202 else: | 1101 else: |
| 1203 tt = ToolTester(report_dir=r.repdir, in_tool_archive=r.newtarpath, new_tool_archive=r.args.new_tool, galaxy_root=args.galaxy_root, include_tests=False) | 1102 tt = ToolTester(report_dir=r.repdir, in_tool_archive=r.newtarpath, new_tool_archive=r.args.new_tool, galaxy_root=args.galaxy_root, include_tests=False) |
| 1204 if args.install: | 1103 if args.install: |
| 1205 #try: | 1104 #try: |
| 1206 tcu = ToolConfUpdater(args=args, tool_dir=os.path.join(args.galaxy_root,args.local_tools), | 1105 tcu = ToolConfUpdater(args=args, tool_dir=args.local_tools, |
| 1207 new_tool_archive_path=r.newtarpath, tool_conf_path=os.path.join(args.galaxy_root,'config','tool_conf.xml'), | 1106 new_tool_archive_path=r.newtarpath, tool_conf_path=args.tool_conf_path, |
| 1208 new_tool_name=r.tool_name) | 1107 new_tool_name=r.tool_name) |
| 1209 #except Exception: | 1108 #except Exception: |
| 1210 # print("### Unable to install the new tool. Are you sure you have all the required special settings?") | 1109 # print("### Unable to install the new tool. Are you sure you have all the required special settings?") |
| 1211 | 1110 |
| 1212 if __name__ == "__main__": | 1111 if __name__ == "__main__": |
