diff toolfactory/ToolFactory_tester.xml @ 0:f288fab71d8b draft default tip

Uploaded
author fubar2
date Mon, 26 Apr 2021 04:18:54 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolfactory/ToolFactory_tester.xml	Mon Apr 26 04:18:54 2021 +0000
@@ -0,0 +1,455 @@
+<tool name="toolfactory_tester" id="toolfactory_tester" version="1">
+  <!--Source in git at: https://github.com/fubar2/toolfactory-->
+  <!--Created by admin@galaxy.org at 23/04/2021 10:25:58 using the Galaxy Tool Factory.-->
+  <description>Test an untested tool and update it</description>
+  <requirements>
+    <requirement type="package" version="2.30.2">git</requirement>
+    <requirement type="package" version="0.74.3">planemo</requirement>  </requirements>
+  <stdio>
+    <exit_code range="1:" level="fatal"/>
+  </stdio>
+  <version_command><![CDATA[echo "1"]]></version_command>
+  <command><![CDATA[
+python
+$runme
+--in_tool_archive
+$in_tool_archive
+--new_tested_tool_archive
+$new_tested_tool_archive
+--galaxy_root
+"$galaxyroot"
+>
+$tf_archive_tester_log;
+]]></command>
+  <configfiles>
+    <configfile name="runme"><![CDATA[#raw
+
+# see https://github.com/fubar2/toolfactory
+#
+# copyright ross lazarus (ross stop lazarus at gmail stop com) May 2012
+#
+# all rights reserved
+# Licensed under the LGPL
+# suggestions for improvement and bug fixes welcome at
+# https://github.com/fubar2/toolfactory
+#
+# July 2020: BCC was fun and I feel like rip van winkle after 5 years.
+# Decided to
+# 1. Fix the toolfactory so it works - done for simplest case
+# 2. Fix planemo so the toolfactory function works
+# 3. Rewrite bits using galaxyxml functions where that makes sense - done
+
+import argparse
+import copy
+import os
+import subprocess
+import shutil
+import sys
+import tarfile
+import tempfile
+import time
+import xml.etree.ElementTree as ET
+
+
+myversion = "V2.2 April 2021"
+verbose = True
+debug = True
+toolFactoryURL = "https://github.com/fubar2/toolfactory"
+
+def timenow():
+    """return current time as a string"""
+    return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time()))
+
+class ToolTester():
+    # requires highly insecure docker settings - like write to tool_conf.xml and to tools !
+    # if in a container possibly not so courageous.
+    # Fine on your own laptop but security red flag for most production instances
+    # uncompress passed tar, run planemo and rebuild a new tarball with tests
+
+    def __init__(self, args=None, in_tool_archive='/galaxy-central/tools/newtool/newtool_toolshed.gz', new_tool_archive=None):
+        self.args = args
+        self.new_tool_archive = new_tool_archive
+        assert tarfile.is_tarfile(in_tool_archive)
+        # this is not going to go well with arbitrary names.
+        tff = tarfile.open(in_tool_archive, "r:*")
+        flist = tff.getnames()
+        ourdir = os.path.commonpath(flist) # eg pyrevpos
+        self.tool_name = ourdir
+        ourxmls = [x for x in flist if x.lower().endswith('.xml') and os.path.split(x)[0] == ourdir]
+        # planemo_test/planemo_test.xml
+        assert len(ourxmls) > 0
+        self.ourxmls = ourxmls # [os.path.join(tool_path,x) for x in ourxmls]
+        res = tff.extractall()
+        self.update_tests(ourdir)
+        tff.close()
+        self.tooloutdir = "./tfout"
+        self.repdir = "./TF_run_report"
+        self.testdir = os.path.join(self.tooloutdir, "test-data")
+        if not os.path.exists(self.tooloutdir):
+            os.mkdir(self.tooloutdir)
+        if not os.path.exists(self.testdir):
+            os.mkdir(self.testdir)
+        if not os.path.exists(self.repdir):
+            os.mkdir(self.repdir)
+        self.makeTool()
+        self.moveRunOutputs()
+        self.makeToolTar()
+
+    def call_planemo(self,xmlpath,ourdir):
+        penv = os.environ
+        #penv['HOME'] = os.path.join(self.args.galaxy_root,'planemo')
+        #penv["GALAXY_VIRTUAL_ENV"] = os.path.join(penv['HOME'],'.planemo','gx_venv_3.9')
+        penv["PIP_CACHE_DIR"] = os.path.join(self.args.galaxy_root,'pipcache')
+        toolfile = os.path.split(xmlpath)[1]
+        tool_name = self.tool_name
+        tool_test_output = f"{tool_name}_planemo_test_report.html"
+        cll = [
+            "planemo",
+            "test",
+            "--biocontainers",
+            "--test_output",
+            os.path.abspath(tool_test_output),
+            "--galaxy_root",
+            self.args.galaxy_root,
+            "--update_test_data",
+            os.path.abspath(xmlpath),
+        ]
+        print(cll)
+        p = subprocess.run(
+            cll,
+            #capture_output=True,
+            encoding='utf8',
+            env = penv,
+            shell=False,
+        )
+        return p
+
+    def makeTool(self):
+        """write xmls and input samples into place"""
+        for xreal in self.ourxmls:
+            x = os.path.split(xreal)[1]
+            xout = os.path.join(self.tooloutdir,x)
+            shutil.copyfile(xreal, xout)
+        # for p in self.infiles:
+            # pth = p["name"]
+            # dest = os.path.join(self.testdir, "%s_sample" % p["infilename"])
+            # shutil.copyfile(pth, dest)
+            # dest = os.path.join(self.repdir, "%s_sample" % p["infilename"])
+            # shutil.copyfile(pth, dest)
+
+    def makeToolTar(self):
+        """move outputs into test-data and prepare the tarball"""
+        excludeme = "_planemo_test_report.html"
+
+        def exclude_function(tarinfo):
+            filename = tarinfo.name
+            return None if filename.endswith(excludeme) else tarinfo
+
+        newtar = 'new_%s_toolshed.gz' % self.tool_name
+        ttf = tarfile.open(newtar, "w:gz")
+        ttf.add(name=self.tool_name,
+            arcname=self.tool_name,
+            filter=exclude_function)
+        ttf.close()
+        shutil.copyfile(newtar, self.new_tool_archive)
+
+    def moveRunOutputs(self):
+        """need to move planemo or run outputs into toolfactory collection"""
+        with os.scandir(self.tooloutdir) as outs:
+            for entry in outs:
+                if not entry.is_file():
+                    continue
+                if "." in entry.name:
+                    _, ext = os.path.splitext(entry.name)
+                    if ext in [".tgz", ".json"]:
+                        continue
+                    if ext in [".yml", ".xml", ".yaml"]:
+                        newname = f"{entry.name.replace('.','_')}.txt"
+                    else:
+                        newname = entry.name
+                else:
+                    newname = f"{entry.name}.txt"
+                dest = os.path.join(self.repdir, newname)
+                src = os.path.join(self.tooloutdir, entry.name)
+                shutil.copyfile(src, dest)
+        with os.scandir('.') as outs:
+            for entry in outs:
+                if not entry.is_file():
+                    continue
+                if "." in entry.name:
+                    _, ext = os.path.splitext(entry.name)
+                    if ext in [".yml", ".xml", ".yaml"]:
+                        newname = f"{entry.name.replace('.','_')}.txt"
+                    else:
+                        newname = entry.name
+                else:
+                    newname = f"{entry.name}.txt"
+                dest = os.path.join(self.repdir, newname)
+                src =entry.name
+                shutil.copyfile(src, dest)
+        if True or self.args.include_tests:
+            with os.scandir(self.testdir) as outs:
+                for entry in outs:
+                    if (not entry.is_file()) or entry.name.endswith(
+                        "_planemo_test_report.html"
+                    ):
+                        continue
+                    if "." in entry.name:
+                        _, ext = os.path.splitext(entry.name)
+                        if ext in [".tgz", ".json"]:
+                            continue
+                        if ext in [".yml", ".xml", ".yaml"]:
+                            newname = f"{entry.name.replace('.','_')}.txt"
+                        else:
+                            newname = entry.name
+                    else:
+                        newname = f"{entry.name}.txt"
+                    dest = os.path.join(self.repdir, newname)
+                    src = os.path.join(self.testdir, entry.name)
+                    shutil.copyfile(src, dest)
+
+
+    def update_tests(self,ourdir):
+        for xmlf in self.ourxmls:
+            capture = self.call_planemo(xmlf,ourdir)
+            #sys.stderr.write('%s, stdout=%s, stderr=%s' % (xmlf, capture.stdout, capture.stdout))
+            #print('%s, stdout=%s, stderr=%s' % (capture.stdout, capture.stdout,xmlf))
+
+def main():
+    """
+    This is a Galaxy wrapper.
+    It expects to be called by a special purpose tool.xml
+
+    """
+    parser = argparse.ArgumentParser()
+    a = parser.add_argument
+    a("--in_tool_archive", default=None)
+    a("--new_tested_tool_archive", default=None)
+    a("--galaxy_root", default="/home/ross/gal21/")
+    args = parser.parse_args()
+    print('Hello from',os.getcwd())
+    tt = ToolTester(args=args, in_tool_archive=args.in_tool_archive, new_tool_archive=args.new_tested_tool_archive)
+
+if __name__ == "__main__":
+    main()
+
+
+#end raw]]></configfile>
+  </configfiles>
+  <inputs>
+    <param name="new_tool_name" value="" type="hidden"/>
+    <param name="in_tool_archive" type="data" optional="false" label="Select a no_test tarfile to test and update for a toolshed" help="" format="toolshed.gz" multiple="false"/>
+    <param name="galaxyroot" type="text" value="/home/ross/gal21" label="Galaxy root for planemo to use - MUST be made available in the Galaxy job runner configuration" help=""/>
+  </inputs>
+  <outputs>
+    <data name="new_tested_tool_archive" format="toolshed.gz" label="${in_tool_archive.name.split('_')[0]}_tested_toolshed.gz"  hidden="false"/>
+    <data name="tf_archive_tester_log" format="txt" label="${in_tool_archive.name}_test_log" hidden="false"/>
+    <collection name="TF_run_report" type="list" label="${in_tool_archive.name} test Run reports">
+      <discover_datasets pattern="__name_and_ext__" directory="TF_run_report" visible="false"/>
+    </collection>
+  </outputs>
+  <tests>
+    <test>
+      <output name="new_tested_tool_archive" value="new_tested_tool_archive_sample" compare="sim_size" delta_frac="0.5"/>
+      <output name="tf_archive_tester_log" value="tf_archive_tester_log_sample" compare="sim_size" delta_frac="0.1"/>
+      <param name="in_tool_archive" value="in_tool_archive_sample"/>
+      <param name="galaxyroot" value="/home/ross/gal21"/>
+      <output_collection name="TF_run_report"/>
+    </test>
+  </tests>
+  <help><![CDATA[
+
+**What it Does**
+
+------
+
+Script::
+
+    import argparse
+    import copy
+    import os
+    import subprocess
+    import shutil
+    import sys
+    import tarfile
+    import tempfile
+    import time
+    import xml.etree.ElementTree as ET
+    myversion = "V2.2 April 2021"
+    verbose = True
+    debug = True
+    toolFactoryURL = "https://github.com/fubar2/toolfactory"
+    def timenow():
+        """return current time as a string"""
+        return time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(time.time()))
+
+    class ToolTester():
+        # requires highly insecure docker settings - like write to tool_conf.xml and to tools !
+        # if in a container possibly not so courageous.
+        # Fine on your own laptop but security red flag for most production instances
+        # uncompress passed tar, run planemo and rebuild a new tarball with tests
+        def __init__(self, args=None, in_tool_archive='/galaxy-central/tools/newtool/newtool_toolshed.gz', new_tool_archive=None):
+            self.args = args
+            self.new_tool_archive = new_tool_archive
+            assert tarfile.is_tarfile(in_tool_archive)
+            # this is not going to go well with arbitrary names.
+            tff = tarfile.open(in_tool_archive, "r:*")
+            flist = tff.getnames()
+            ourdir = os.path.commonpath(flist) # eg pyrevpos
+            self.tool_name = ourdir
+            ourxmls = [x for x in flist if x.lower().endswith('.xml') and os.path.split(x)[0] == ourdir]
+            # planemo_test/planemo_test.xml
+            assert len(ourxmls) > 0
+            self.ourxmls = ourxmls # [os.path.join(tool_path,x) for x in ourxmls]
+            res = tff.extractall()
+            self.update_tests(ourdir)
+            tff.close()
+            self.tooloutdir = "./tfout"
+            self.repdir = "./TF_run_report"
+            self.testdir = os.path.join(self.tooloutdir, "test-data")
+            if not os.path.exists(self.tooloutdir):
+                os.mkdir(self.tooloutdir)
+            if not os.path.exists(self.testdir):
+                os.mkdir(self.testdir)
+            if not os.path.exists(self.repdir):
+                os.mkdir(self.repdir)
+            self.makeTool()
+            self.moveRunOutputs()
+            self.makeToolTar()
+
+        def call_planemo(self,xmlpath,ourdir):
+            penv = os.environ
+            penv['HOME'] = '/home/ross/galaxy-release_21.01'
+            toolfile = os.path.split(xmlpath)[1]
+            tool_name = self.tool_name
+            tool_test_output = f"{tool_name}_planemo_test_report.html"
+            cll = [
+                "planemo",
+                "test",
+                "--test_output",
+                os.path.abspath(tool_test_output),
+                "--galaxy_root",
+                self.args.galaxy_root,
+                "--update_test_data",
+                os.path.abspath(xmlpath),
+            ]
+            print(cll)
+            p = subprocess.run(
+                cll,
+                capture_output=True,
+                encoding='utf8',
+                env = penv,
+                shell=False,
+            )
+            return p
+
+        def makeTool(self):
+            """write xmls and input samples into place"""
+            for xreal in self.ourxmls:
+                x = os.path.split(xreal)[1]
+                xout = os.path.join(self.tooloutdir,x)
+                shutil.copyfile(xreal, xout)
+            # for p in self.infiles:
+                # pth = p["name"]
+                # dest = os.path.join(self.testdir, "%s_sample" % p["infilename"])
+                # shutil.copyfile(pth, dest)
+                # dest = os.path.join(self.repdir, "%s_sample" % p["infilename"])
+                # shutil.copyfile(pth, dest)
+
+        def makeToolTar(self):
+            """move outputs into test-data and prepare the tarball"""
+            excludeme = "_planemo_test_report.html"
+            def exclude_function(tarinfo):
+                filename = tarinfo.name
+                return None if filename.endswith(excludeme) else tarinfo
+            newtar = 'new_%s_toolshed.gz' % self.tool_name
+            ttf = tarfile.open(newtar, "w:gz")
+            ttf.add(name=self.tooloutdir,
+                arcname=self.tool_name,
+                filter=exclude_function)
+            ttf.close()
+            shutil.copyfile(newtar, self.new_tool_archive)
+
+        def moveRunOutputs(self):
+            """need to move planemo or run outputs into toolfactory collection"""
+            with os.scandir(self.tooloutdir) as outs:
+                for entry in outs:
+                    if not entry.is_file():
+                        continue
+                    if "." in entry.name:
+                        _, ext = os.path.splitext(entry.name)
+                        if ext in [".tgz", ".json"]:
+                            continue
+                        if ext in [".yml", ".xml", ".yaml"]:
+                            newname = f"{entry.name.replace('.','_')}.txt"
+                        else:
+                            newname = entry.name
+                    else:
+                        newname = f"{entry.name}.txt"
+                    dest = os.path.join(self.repdir, newname)
+                    src = os.path.join(self.tooloutdir, entry.name)
+                    shutil.copyfile(src, dest)
+            with os.scandir('.') as outs:
+                for entry in outs:
+                    if not entry.is_file():
+                        continue
+                    if "." in entry.name:
+                        _, ext = os.path.splitext(entry.name)
+                        if ext in [".yml", ".xml", ".yaml"]:
+                            newname = f"{entry.name.replace('.','_')}.txt"
+                        else:
+                            newname = entry.name
+                    else:
+                        newname = f"{entry.name}.txt"
+                    dest = os.path.join(self.repdir, newname)
+                    src =entry.name
+                    shutil.copyfile(src, dest)
+            if True or self.args.include_tests:
+                with os.scandir(self.testdir) as outs:
+                    for entry in outs:
+                        if (not entry.is_file()) or entry.name.endswith(
+                            "_planemo_test_report.html"
+                        ):
+                            continue
+                        if "." in entry.name:
+                            _, ext = os.path.splitext(entry.name)
+                            if ext in [".tgz", ".json"]:
+                                continue
+                            if ext in [".yml", ".xml", ".yaml"]:
+                                newname = f"{entry.name.replace('.','_')}.txt"
+                            else:
+                                newname = entry.name
+                        else:
+                            newname = f"{entry.name}.txt"
+                        dest = os.path.join(self.repdir, newname)
+                        src = os.path.join(self.testdir, entry.name)
+                        shutil.copyfile(src, dest)
+
+        def update_tests(self,ourdir):
+            for xmlf in self.ourxmls:
+                capture = self.call_planemo(xmlf,ourdir)
+                #sys.stderr.write('%s, stdout=%s, stderr=%s' % (xmlf, capture.stdout, capture.stdout))
+                print('%s, stdout=%s, stderr=%s' % (capture.stdout, capture.stdout,xmlf))
+
+    def main():
+        """
+        This is a Galaxy wrapper.
+        It expects to be called by a special purpose tool.xml
+        """
+        parser = argparse.ArgumentParser()
+        a = parser.add_argument
+        a("--in_tool_archive", default=None)
+        a("--new_tested_tool_archive", default=None)
+        a("--galaxy_root", default="/home/ross/gal21/")
+        args = parser.parse_args()
+        print('Hello from',os.getcwd())
+        tt = ToolTester(args=args, in_tool_archive=args.in_tool_archive, new_tool_archive=args.new_tested_tool_archive)
+    if __name__ == "__main__":
+        main()
+
+]]></help>
+  <citations>
+    <citation type="doi">10.1093/bioinformatics/bts573</citation>
+  </citations>
+</tool>
+