Exemplo n.º 1
0
    def generate_report(self, console, warnings, fa_file_name, params, out_dir,
                        wsname):
        """
        Generating and saving report
        """
        self.log(console, 'Generating and saving report')

        fa_file_with_path = os.path.join(out_dir, fa_file_name)
        [length_stats, coverage_stats,
         circ_stats] = self.load_stats(console, fa_file_with_path)
        lengths = [length_stats[contig_id] for contig_id in length_stats]

        assembly_ref = wsname + '/' + params['output_contigset_name']

        report_text = ''
        report_text += 'Unicycler results saved to: ' + wsname + '/' + out_dir + '\n'
        report_text += 'Assembly saved to: ' + assembly_ref + '\n'
        report_text += 'Assembled into ' + str(len(lengths)) + ' contigs.\n'
        report_text += 'Avg Length: ' + str(
            sum(lengths) / float(len(lengths))) + ' bp.\n'

        # compute a simple contig length distribution
        bins = 10
        counts, edges = np.histogram(lengths, bins)
        report_text += 'Contig Length Distribution (# of contigs -- min to max ' + 'basepairs):\n'
        for c in range(bins):
            report_text += ('   ' + str(counts[c]) + '\t--\t' + str(edges[c]) +
                            ' to ' + str(edges[c + 1]) + ' bp\n')
        self.log(console, 'Running QUAST')
        kbq = kb_quast(self.callbackURL)
        quastret = kbq.run_QUAST({
            'files': [{
                'path': fa_file_with_path,
                'label': params['output_contigset_name']
            }]
        })
        # self.log(console,'quastret = '+pformat(quastret))

        # delete assembly file to keep it out of zip
        os.remove(fa_file_with_path)

        # check starting genes
        in_start = False
        ic = iter(console)
        for line in ic:
            if line.startswith('Rotating completed replicons'):
                while not line.startswith('Assembly complete'):
                    line = next(ic)
                    # self.log(console,'debug line = '+line)
                    fields = line.strip().split()
                    if len(fields) > 3 and fields[
                            0] in circ_stats and circ_stats[fields[0]] == 'Y':
                        if fields[3] == 'none':
                            fields[3] = 'none found'
                        circ_stats[fields[0]] = 'Y, ' + fields[3]

        # check circularization and make data table for report
        contig_data = []
        for contig_id in length_stats:
            contig_data.append({
                'contig_id': contig_id,
                'circular': circ_stats[contig_id],
                'coverage': coverage_stats[contig_id],
                'length': length_stats[contig_id]
            })

        # self.log(console, 'contig_data = '+pformat(contig_data))

        # move quast output into main out_dir
        move(os.path.join(quastret['quast_path'], 'report.html'),
             os.path.join(out_dir, 'quast_report.html'))

        output_files = self.generate_output_file_list(console, out_dir)

        # render template
        template_file = 'unicycler_tabs.tt'
        tmpl_data = {
            'page_title':
            'Unicycler Report',
            'data_array':
            contig_data,
            'cols': [{
                'data': 'contig_id',
                'title': 'Contig ID'
            }, {
                'data': 'circular',
                'title': 'Circular, Starting Gene'
            }, {
                'data': 'coverage',
                'title': 'Coverage (x)'
            }, {
                'data': 'length',
                'title': 'Length (bp)'
            }]
        }
        # tmpl_data['quast_output'] = '<iframe>'+self.read_html(os.path.join(quastret['quast_path'],'report.html'))+'</iframe>'
        # tmpl_data['quast_output'] = '<iframe frameborder="0" width="100%" height="100%" src="'+os.path.join(quastret['quast_path'],'report.html')+'"></iframe>'
        tmpl_data[
            'quast_output'] = '<iframe style="display:block; width:100%; height:100vh; border:none;" src="quast_report.html"></iframe>'
        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)
        tmpl_data['unicycler_log'] = '<p><pre>' + '<br>'.join(
            filter(
                lambda line: not (line.startswith('tput') or line.lstrip().
                                  startswith('0 / ')), console)) + '</pre></p>'

        # save report
        self.log(console, 'Saving report')
        report_file = 'unicycler_report.html'

        # copy the templates into 'scratch', where they can be accessed by KBaseReport
        try:
            copytree(os.path.join(self.appdir, 'templates'),
                     os.path.join(self.scratch, 'templates'))
        except Exception as e:
            self.log(console, 'Exception copying tree. ' + str(e))

        reportClient = KBaseReport(self.callbackURL)
        template_output = reportClient.render_template({
            'template_file':
            os.path.join(self.scratch, 'templates', template_file),
            'template_data_json':
            json.dumps(tmpl_data),
            'output_file':
            os.path.join(out_dir, report_file)
        })

        report_output = reportClient.create_extended_report({
            'message':
            report_text,
            'objects_created': [{
                'ref': assembly_ref,
                'description': 'Assembled contigs'
            }],
            'direct_html_link_index':
            0,
            'file_links':
            output_files,
            'html_links': [{
                'path': out_dir,
                'name': report_file,
                'label': 'Unicycler report',
                'description': 'description of template report'
            }],
            'warnings':
            warnings,
            'report_object_name':
            'kb_unicycler_report_' + str(uuid.uuid4()),
            'workspace_name':
            params['workspace_name']
        })

        return report_output['name'], report_output['ref']
Exemplo n.º 2
0
class MakeTemplates(LogMixin, object):
    '''
    Module Name:
    MakeTemplates

    Module Description:
    A module for making templates
    '''
    def __init__(self, config):
        self.callback_url = os.environ['SDK_CALLBACK_URL']
        self.scratch = config['scratch']
        self.appdir = config['appdir']
        logging.basicConfig(format='%(name)s %(levelname)s %(message)s',
                            level=logging.DEBUG)
        self.reporter = KBaseReport(self.callback_url, service_ver='dev')

    def edge_data_cols(self):
        '''
        definition of table columns for DataTables.js
        '''

        return [
            {
                'data': 'node1',
                'title': 'Source node'
            },
            {
                'data': 'node2',
                'title': 'Target node'
            },
            {
                'data': 'edge',
                'title': 'Edge weight'
            },
            {
                'data': 'edge_descrip',
                'title': 'Edge description'
            },
            {
                'data': 'layer_descrip',
                'title': 'Layer description'
            },
        ]

    def read_template(self, template_name):
        '''
        read in a template file and escape all html content

        used to display template contents
        '''

        with open(os.path.join(self.appdir, 'templates',
                               template_name)) as file:
            lines = file.read()

        # escape all the html, display the results
        escaped_lines = escape(lines, quote=True)

        return escaped_lines

    def validate_params(self, params):

        if not params:
            raise ValueError(
                'Missing required parameters workspace_id and report_type')

        if 'workspace_id' not in params or not params['workspace_id']:
            raise ValueError(
                'Required parameter workspace_id is not set or is invalid')

        valid_report_types = ['single_table', 'table_and_image']

        if 'report_type' not in params or params[
                'report_type'] not in valid_report_types:
            raise ValueError(
                'Required parameter report_type is not set or is invalid')

    def make_templates(self, params):
        '''
        create HTML reports from templates using a variety of methods
        '''

        self.validate_params(params)

        self.logger.info({'KBaseReport version': self.reporter._service_ver})

        # copy the templates into 'scratch', where they can be accessed by KBaseReport
        copytree(os.path.join(self.appdir, 'templates'),
                 os.path.join(self.scratch, 'templates'))

        if params['report_type'] == 'single_table':
            html_links = [
                self.create_report_in_directory('json'),
                self.create_report_in_directory('tsv'),
                self.standalone_report_array_of_arrays(),
                self.standalone_report_array_of_objects(),
            ]
        elif params['report_type'] == 'table_and_image':
            html_links = [
                self.table_and_image_report_local_files(),
                self.table_and_image_report_no_files(),
            ]

        result = self.reporter.create_extended_report({
            'html_links':
            html_links,
            'direct_html_link_index':
            0,
            'report_object_name':
            'My_Cool_Report',
            'workspace_name':
            params['workspace_name'],
        })

        self.logger.debug({'combined report': result})
        return {
            'report_name': result['name'],
            'report_ref': result['ref'],
        }

    def standalone_report_array_of_arrays(self):
        '''
        render a report with an array of arrays as table data
        '''

        source_file = os.path.join(self.appdir, 'data', 'edge_data.tsv')
        template_file = os.path.join(self.scratch, 'templates', 'edge_data.tt')

        # read in a file, splitting on '\n' and then '\t' to create a list of lists
        with open(source_file, 'r') as read_fh:
            lines = list(
                map(lambda x: x.split('\t'),
                    read_fh.read().rstrip().split('\n')))

        tmpl_data = {
            'page_title':
            'Edge Data Table, from an array of arrays',
            'table_tab_title':
            'Edge Data',
            # lines[0] is the header line
            # the rest of the lines are the data points
            'data_array':
            lines[1:],
            # formatted column names in the order they appear in the header line
            'cols': [
                {
                    'title': 'Source node'
                },
                {
                    'title': 'Target node'
                },
                {
                    'title': 'Edge weight'
                },
                {
                    'title': 'Edge description'
                },
                {
                    'title': 'Layer description'
                },
            ],
        }

        # for debugging
        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)

        return {
            'name': 'standalone_aoa_report.html',
            'template': {
                'template_file': template_file,
                'template_data_json': json.dumps(tmpl_data),
            },
            'description': 'HTML report with data from controller',
        }

    def standalone_report_array_of_objects(self):
        '''
        render a report with an array of objects (dicts) as table data
        '''

        template_file = os.path.join(self.scratch, 'templates', 'edge_data.tt')
        edge_data_object_array = self._get_json_report_data()

        tmpl_data = {
            'page_title': 'Edge Data Table, from an array of objects',
            'table_tab_title': 'Edge Data',
            'data_array': edge_data_object_array,
            # our data is in the form of an array of objects [dicts] with the key/value pairs that
            # appeared in the original file. To make it more viewer-friendly, remap the
            # names in the object to appropriate column headers
            'cols': self.edge_data_cols(),
        }

        # for debugging
        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)

        return {
            'name': 'standalone_aoo_report.html',
            'template': {
                'template_file': template_file,
                'template_data_json': json.dumps(tmpl_data),
            },
            'description': 'HTML report with data from controller',
        }

    def create_report_in_directory(self, file_format):
        '''
        Create a report that runs off a data file in format `file_format`

        The report HTML page must be rendered first, and then the rendered page supplied,
        along with the data file and any other supporting files, as a directory to KBaseReport.
        This allows HTML links to files in that directory to work correctly.
        '''
        output_dir = os.path.join(self.scratch, file_format + '_output_dir')
        report_file = file_format + '_report.html'
        data_file = 'edge_data.' + file_format
        template_file = os.path.join(self.scratch, 'templates', 'edge_data.tt')
        report_description = 'HTML report, ' + file_format + ' data source, with supporting files'

        tmpl_data = {
            'page_title': 'Edge Table, data from a ' + file_format + ' file',
            'table_tab_title': 'Edge Data',
            'data_file': data_file,
            'data_file_format': file_format,
            'cols': self.edge_data_cols(),
        }

        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)

        # create the output directory
        os.makedirs(output_dir)

        # render the report template
        result = self.reporter.render_template({
            'template_file':
            template_file,
            'template_data_json':
            json.dumps(tmpl_data),
            'output_file':
            os.path.join(output_dir, report_file)
        })

        self.logger.info(result)

        # copy any supporting files into the directory
        # these might also include images, JS, or CSS files needed to render the page,
        # as well as data files
        copy(os.path.join(self.appdir, 'data', data_file),
             os.path.join(output_dir, data_file))

        # add the directory containing the report and supporting files to html_links
        return {
            'path': output_dir,
            'name': report_file,
            'description': report_description,
        }

    def table_and_image_report_local_files(self):
        '''
        Create a report with two tabs, one containing tabular data, and the other, a captioned image.

        If a local image is used (e.g. output from an app), the report HTML page must be rendered
        first, and then the rendered page supplied, along with the image file, as a directory to
        KBaseReport.
        '''

        output_dir = os.path.join(self.scratch, 'table_image_output_dir')
        report_file = 'table_local_image_report.html'
        template_file = os.path.join(self.scratch, 'templates',
                                     'table_and_image_tabs.tt')
        report_description = 'HTML report, table and image tabs, with supporting files'

        # local image file
        local_image_file = 'logo.png'
        local_image_caption = '<p>The <a href="http://kbase.us">KBase logo</a>, in PNG format. This file was uploaded as part of the report.</p>'

        # data for the table
        edge_data_object_array = self._get_json_report_data()

        # remote image file
        tmpl_data = {
            'page_title': 'Edge Table and Image Report',
            'table_tab_title': 'Edge Data, from an array of objects',
            'data_array': edge_data_object_array,
            'cols': self.edge_data_cols(),
            # image information
            'image_tab_title': 'Local image',
            'image_config_img_src': local_image_file,
            'image_config_img_alt': 'KBase logo',
            'image_config_caption': local_image_caption,
        }

        # for debugging
        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)

        # create the output directory
        os.makedirs(output_dir)

        # render the report template
        result = self.reporter.render_template({
            'template_file':
            template_file,
            'template_data_json':
            json.dumps(tmpl_data),
            'output_file':
            os.path.join(output_dir, report_file)
        })

        self.logger.info(result)

        # copy any supporting files into the directory
        # these might also include images, JS, or CSS files needed to render the page,
        # as well as data files
        copy(os.path.join(self.appdir, 'data', local_image_file),
             os.path.join(output_dir, local_image_file))

        # add the directory containing the report and supporting files to html_links
        return {
            'path': output_dir,
            'name': report_file,
            'description': report_description,
        }

    def table_and_image_report_no_files(self):
        '''
        Create a report with two tabs, one containing tabular data, and the other, a captioned image.

        This report does not use any local files, so the template does not need to be rendered in
        advance.
        '''

        report_file = 'table_remote_image_report.html'
        template_file = os.path.join(self.scratch, 'templates',
                                     'table_and_image_tabs.tt')
        report_description = 'HTML report, table and image tabs, with supporting files'

        # image file hosted elsewhere
        remote_image_file = 'https://kbase.github.io/kb_sdk_docs/_static/logo.png'
        remote_image_caption = '<p>The <a href="http://kbase.us">KBase logo</a>, in PNG format. This file is hosted on kbase.github.io</p>'

        edge_data_object_array = self._get_json_report_data()

        # remote image file
        tmpl_data = {
            'page_title': 'Edge Table and Image Report',
            'table_tab_title': 'Edge Data, from an array of objects',
            'data_array': edge_data_object_array,
            'cols': self.edge_data_cols(),
            # image information
            'image_tab_title': 'Remote image',
            'image_config_img_src': remote_image_file,
            'image_config_img_alt': 'KBase logo',
            'image_config_caption': remote_image_caption,
        }

        # for debugging
        tmpl_data['tmpl_vars'] = json.dumps(tmpl_data,
                                            sort_keys=True,
                                            indent=2)
        tmpl_data['template_content'] = self.read_template(template_file)

        # add the directory containing the report and supporting files to html_links
        return {
            'template': {
                'template_file': template_file,
                'template_data_json': json.dumps(tmpl_data),
            },
            'name': report_file,
            'description': report_description,
        }

    def _get_json_report_data(self):
        '''
        read in a JSON file containing edge data as an array of JSON objects
        '''

        source_file = os.path.join(self.appdir, 'data', 'edge_data.json')

        # read in lines and parse the JSON to convert to py data structures (list of dicts)
        with open(source_file, 'r') as read_fh:
            lines = read_fh.read().rstrip()

        return json.loads(lines)