Plugin Examples

This section has a very basic example plugin and its code.

Basic Plugin

This plugin goes through the explog and presents a few entries in the plugin output. It produces an HTML output but does not take any parameters.

#!/usr/bin/python
# Copyright (C) 2018 Thermo Fisher Scientific, Inc. All Rights Reserved

#
# Samples: LogParser
# plugin demonstrating simple log parsing ability
#

import json
import os
from django.utils.functional import cached_property
from ion.plugin import *


class LogParser(IonPlugin):
    # The version number for this plugin
    version = "5.4.0.0"

    # this plugin can run on fullchip runs, thumbnail runs, and composite (merged via project page) runs
    # when this plugin is manually launched, only the 'launch' method is called
    runtypes = [RunType.FULLCHIP, RunType.THUMB, RunType.COMPOSITE]

    # specify when the plugin is called.  For log parsing, stay simple and just get called when the run completes.
    # the plugin can also be called before the run starts, at the block level, or after all other default plugins run
    runlevels = [RunLevel.DEFAULT]

    # a simple cached version of the start plugin property
    @cached_property
    def startplugin_json(self):
        return self.startplugin

    def read_explog(self):
        """This method reads and outputs an array of colon-delimited key/value pairs from the explog_final.txt"""

        path = os.path.join(self.startplugin_json['runinfo']['raw_data_dir'], "explog_final.txt")
        if not os.path.exists(path):
            raise Exception("explog_final.txt missing")

        # parse the log file for all of the values in a colon-delimited parameter
        data = dict()
        for line in open(path):
            # accommodates formatting issues in explog
            datum = line.split(":", 1)
            if len(datum) == 2:
                key, value = datum
                data[key.strip()] = value.strip()

        return data

    def launch(self, data=None):
        """This is the primary launch method for the plugin."""

        na = '<strong>NA</strong>'
        # creates a results object that is written out later.  This holds data that can be scrapped by a LIMS system,
                    and will be part of the |TS| database
        exp_log_data = self.read_explog()
        results_json = {
            'Project': exp_log_data.get('Project', None) or na,
            'Sample': exp_log_data.get('Sample', None) or na,
            'Library': exp_log_data.get('Library', None) or na,
        }

        # open up an HTML file to dump interesting log file findings to
        with open(self.startplugin_json['runinfo']['results_dir'] + '/LogParser_block.html', 'w') as html_handle:
            html_handle.write('<html><body>')
            html_handle.write("Project is: %s<br \>" % results_json['Project'])
            html_handle.write("Sample is: %s<br \>" % results_json['Sample'])
            html_handle.write("Library is: %s<br \>" % results_json['Library'])
            html_handle.write('</body></hmtl>')

        # write out our results json object
        with open(self.startplugin_json['runinfo']['results_dir'] + '/results.json', 'w') as results_handle:
            json.dump(results_json, results_handle, indent=4)

        return True

# Devel use - running directly
if __name__ == "__main__":
    PluginCLI()

Plugin which uses a subprocess

#!/usr/bin/python
# Copyright (C) 2018 Thermo Fisher Scientific, Inc. All Rights Reserved

#
# Samples: LogParser
# plugin demonstrating simple log parsing ability
#

import json
import os
from django.utils.functional import cached_property
from ion.plugin import *
from subprocess import check_output


class CallSubprocessExample(IonPlugin):
    # The version number for this plugin
    version = "5.4.0.0"

    # this plugin can run on fullchip runs, thumbnail runs, and composite (merged via project page) runs
    # note that when the plugin is manually launched, only the 'launch' method will be called
    runtypes = [RunType.FULLCHIP, RunType.THUMB, RunType.COMPOSITE]

    # specify when the plugin is called.  For log parsing, stay simple and just get called when the run completes.
    # but can also be called before the run starts, at the block level, or after all other default plugins run
    runlevels = [RunLevel.DEFAULT]

    # a simple cached version of the start plugin property
    @cached_property
    def startplugin_json(self):
        return self.startplugin

    def launch(self, data=None):
        """This is the primary launch method for the plugin."""

        path_to_executable = "MyExecutable"
        arg1 = 'First Argument'
        arg2 = 'Second Argument'
        results = check_output([path_to_executable, arg1, arg2], cwd=self.startplugin_json['runinfo']['plugin']['path'])

        return True

# Devel use - running directly
if __name__ == "__main__":
    PluginCLI()

Accessing Barcode Data

#!/usr/bin/python
# Copyright (C) 2018 Thermo Fisher Scientific, Inc. All Rights Reserved

#
# Samples: LogParser
# plugin demonstrating simple log parsing ability
#

import json
import os
from django.utils.functional import cached_property
from ion.plugin import *
from subprocess import check_output


class BarcodesExample(IonPlugin):
    # The version number for this plugin
    version = "5.4.0.0"

    # this plugin can run on fullchip runs, thumbnail runs, and composite (merged via project page) runs
    # note that when the plugin is manually launched, only the 'launch' method will be called
    runtypes = [RunType.FULLCHIP, RunType.THUMB, RunType.COMPOSITE]

    # specify when the plugin is called.  For log parsing, stay simple and just get called when the run completes.
    # but can also be called before the run starts, at the block level, or after all other default plugins run
    runlevels = [RunLevel.DEFAULT]

    # a simple cached version of the start plugin property
    @cached_property
    def startplugin_json(self):
        return self.startplugin

    @cached_property
    def barcodes_json:
        with open('barcodes.json', 'r') as barcodes_handle:
            return json.load(barcodes_handle)

    def launch(self, data=None):
        """This is the primary launch method for the plugin."""

        for barcode_name, barcode_values in self.barcodes_json.iteritems():
            # do you work per barcode here!
            print("Barcode Name: " + barcode_name)
            print("Bam File: " + barcode_values['bam_file']

        return True

# Devel use - running directly
if __name__ == "__main__":
    PluginCLI()

Using the REST API

import json
import requests
api_response = requests.get('http://HOSTNAME/APPNAME/api/v1/APIENDPOINT/?ARG1=VAL1&pluginresult=self.startplugin['runinfo']['pluginresult']&api_key=' + self.startplugin['runinfo']['api_key'])
api_response.raise_for_status()
api_json_response = json.loads(api_response.content)

Note: api-key is tied to the specific plugin run so the system must also get which “pluginresult” is sending the API request.

Example 1: How to get the installed annotation files during the plugin execution?

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2018 Thermo Fisher Scientific, Inc. All Rights Reserved

import os
import sys
import requests
import json

from ion.plugin import RunType, PluginCLI, IonPlugin

API_VER = '/v1'

class PluginApiTest(IonPlugin):
    """
       This will make REST api call to get the installed annotation files
       with 'api_key' corresponding to the specific plugin run
    """

    version = "1.0"
    runtypes = [RunType.THUMB, RunType.FULLCHIP]

    def launch(self):
        sp_json_fpath = os.path.join(
            os.environ.get('RESULTS_DIR', ''),
            'startplugin.json'
        )

        with open(sp_json_fpath, 'r') as sp_fh:
            sp_json = json.load(sp_fh)

        endpoint = '/content'
        auth_params = {
            'api_key': sp_json['runinfo']['api_key'],
            'pluginresult': sp_json['runinfo']['pluginresult'],
        }
        query_params = {
            'type': 'Annotation'
        }
        request_params = {}
        request_params.update(auth_params)
        request_params.update(query_params)

        url = sp_json['runinfo']['api_url'] + API_VER + endpoint
        resp = requests.get(url, params=request_params)
        if resp:
            print(resp.url)
            print(resp.json())
            sys.exit(0)
        else:
            print(resp.status_code)
            sys.exit(1)


if __name__ == "__main__":
    PluginCLI()

Example 2: Use the below query_params to list all the files of mm10 auxiliary references for RNASeqAnalysis. Please note the type of camelcase, AuxiliaryReference.

...
query_params = {
    'application_tags__icontains' : 'RNA',
    'extra':'mm10',
    'type' :'AuxiliaryReference'
}
...

Displaying Progress

When displaying progress, this needs to be manually updated by re-writing the *_block.html file intermittently during the process. Here is a simple example of how to construct a method to do this.

...
def update_progress(self, current_progress, max_progress)
    with open(self.startplugin['runinfo']['results_dir'] + '/progress_block.html', 'w') as html_handle:
            html_handle.write('<html><body>')
            html_handle.write("The current progress is %d of %d" %(current_progress, max_progress))
            html_handle.write('</body></hmtl>')
...

Rendering Templates

When outputting HTML files, using templates can be cleaner than assembling long strings. Below is an example of using Django templates. This example uses an HTML template named progress_block_template.html inside a templates directory inside the plugins root directory.

from django.conf import settings
from django.template.loader import render_to_string

settings.configure(TEMPLATE_DIR=self.startplugin["runinfo"]["plugin"]["path"] + '/templates')

with open(self.startplugin['runinfo']['results_dir'] + '/progress_block.html', 'w') as html_handle:
    html_handle.write(render_to_string("progress_block_template.html", {"current_progress": 54}))