File: pycoQC_report.py

package info (click to toggle)
pycoqc 2.5.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 98,704 kB
  • sloc: python: 2,295; sh: 165; makefile: 5
file content (195 lines) | stat: -rw-r--r-- 7,292 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#~~~~~~~~~~~~~~IMPORTS~~~~~~~~~~~~~~#
# Standard library imports
import json
from pkg_resources import resource_filename
import datetime
import os

# Third party imports
import plotly.offline as py
import jinja2

# Local imports
from pycoQC.common import *
from pycoQC.pycoQC_parse import pycoQC_parse
from pycoQC.pycoQC_plot import pycoQC_plot
from pycoQC import __version__ as package_version
from pycoQC import __name__ as package_name

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~MAIN CLASS~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
class pycoQC_report ():

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~INIT METHOD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
    def __init__ (self,
        parser:pycoQC_parse,
        plotter:pycoQC_plot,
        verbose:bool=False,
        quiet:bool=False):
        """
        * parser
            A pycoQC_parse object
        * plotter
            A pycoQC_plot object
        * verbose
            Increase verbosity
        * quiet
            Reduce verbosity
        """
        # Set logging level
        self.logger = get_logger (name=__name__, verbose=verbose, quiet=quiet)

        # Check that parser is a valid instance of pycoQC_parse
        if not isinstance(parser, pycoQC_parse):
            raise pycoQCError ("{} is not a valid pycoQC_parse object".format(parser))
        self.parser = parser

        # Check that plotter is a valid instance of pycoQC_plot
        if not isinstance(plotter, pycoQC_plot):
            raise pycoQCError ("{} is not a valid pycoQC_plot object".format(plotter))
        self.plotter = plotter

    def __repr__(self):
        return "[{}]\n".format(self.__class__.__name__)

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC METHODS~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
    def html_report( self,
        outfile:str,
        config_file:str="",
        template_file:str="",
        report_title:str="PycoQC report",
        skip_coverage_plot:bool=False):
        """"""
        self.logger.info("Generating HTML report")

        # Parse configuration file
        self.logger.info("\tParsing html config file")
        config_dict = self._get_config(config_file)
        self.logger.debug(config_dict)

        # Loop over configuration file and run the pycoQC functions defined
        plots = list()
        titles = list()
        for method_name, method_args in config_dict.items ():
            if skip_coverage_plot and method_name == "alignment_coverage":
                self.logger.info("\tSkipping method {}".format(method_name))
                continue
            try:
                self.logger.info("\tRunning method {}".format(method_name))
                self.logger.debug ("\t{} ({})".format(method_name, method_args))

                # Store plot title for HTML title and remove from data passed to plotly
                plot_title = method_args["plot_title"]
                method_args["plot_title"]=""

                # Get method and generate plot
                method = getattr(self.plotter, method_name)
                fig = method(**method_args)
                plot = py.plot(
                    fig,
                    output_type='div',
                    include_plotlyjs=False,
                    image_width='',
                    image_height='',
                    show_link=False,
                    auto_open=False)

                plots.append(plot)
                titles.append(plot_title)

            except AttributeError as E:
                self.logger.info("\t\t{} is not a valid plotting method".format(method_name))
                self.logger.info("\t\t{}".format(E))

            except pycoQCError as E:
                self.logger.info("\t\t{}".format(E))

        # Load HTML template for Jinja
        self.logger.info("\tLoading HTML template")
        template = self._get_jinja_template(template_file)

        # Set a subtitle for the HTML report
        report_subtitle="Generated on {} with {} {}".format( datetime.datetime.now().strftime("%d/%m/%y"), package_name, package_version)

        # Define source files list
        src_files = ""
        for files_list, name in (
            (self.parser.summary_files_list,"summary"),
            (self.parser.barcode_files_list,"barcode"),
            (self.parser.bam_file_list,"bam")):

            if files_list:
                src_files += "<h4>Source {} files</h4><ul>".format(name)
                for f in files_list:
                    f = os.path.abspath(f)
                    src_files += "<li>{}</li>".format(f)
                src_files += "</ul>"

        # Render plots
        self.logger.info("\tRendering plots in d3js")
        rendering = template.render(
            plots=plots,
            titles=titles,
            plotlyjs=py.get_plotlyjs(),
            report_title=report_title,
            report_subtitle=report_subtitle,
            src_files=src_files)

        # Write to HTML file
        self.logger.info("\tWriting to HTML file")
        mkbasedir(outfile, exist_ok=True)
        with open(outfile, "w") as fp:
            fp.write(rendering)

    def json_report(self,
        outfile:str):
        """"""
        self.logger.info("Generating JSON report")
        self.logger.info("\tRunning summary_stats_dict method")
        res_dict = self.plotter.summary_stats_dict ()

        self.logger.info("\tWriting to JSON file")
        mkbasedir(outfile, exist_ok=True)
        with open (outfile, "w") as fp:
            json.dump(res_dict, fp, indent=2)

    #~~~~~~~~~~~~~~PRIVATE FUNCTION~~~~~~~~~~~~~~#

    def _get_config(self, config_file=None):
        """"""
        # First, try to read provided configuration file if given
        if config_file:
            self.logger.debug ("\tTry to read provided config file")
            try:
                with open(config_file, 'r') as cf:
                    return json.load(cf)
            except (FileNotFoundError, IOError, json.JSONDecodeError):
                self.logger.debug ("\t\tConfiguration file not found, non-readable or invalid")

        # Last use the default harcoded config_dict
        self.logger.debug ("\tRead default configuration file")
        config_file = resource_filename("pycoQC", "templates/pycoQC_config.json")
        with open(config_file, 'r') as cf:
            return json.load(cf)

    def _get_jinja_template(self, template_file=None):
        """"""
        # First, try to read provided configuration file if given
        if template_file:
            self.logger.debug("\tTry to load provided jinja template file")
            try:
                with open(template_file) as fp:
                    template = jinja2.Template(fp.read())
                    return template
            except (FileNotFoundError, IOError, jinja2.exceptions.TemplateNotFound, jinja2.exceptions.TemplateSyntaxError):
                self.logger.debug ("\t\tFile not found, non-readable or invalid")

        # Last use the default harcoded config_dict
        self.logger.debug ("\tRead default jinja template")
        env = jinja2.Environment (
            loader=jinja2.PackageLoader('pycoQC', 'templates'),
            autoescape=jinja2.select_autoescape(["html"]))
        template = env.get_template('spectre.html.j2')
        return template