Skip to content

output

FitReport

Bases: FitResultPlotBase

Create .pdf output of a fit result

Source code in pyhdx/output.py
 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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
class FitReport(FitResultPlotBase):
    """
    Create .pdf output of a fit result
    """

    def __init__(self, fit_result, title=None, doc=None, add_date=True, temp_dir=None, **kwargs):
        super().__init__(fit_result)
        self.title = title or "Fit report"
        self.doc = doc or self._init_doc(add_date=add_date)
        self._temp_dir = temp_dir or self.make_temp_dir()
        self._temp_dir = Path(self._temp_dir)

        self.figure_queue = []
        self.tex_dict = (
            {}
        )  # dictionary gathering lists of partial functions which when executed generate the tex output
        self._figure_number = 0  # todo automate

    def make_temp_dir(self):
        # todo pathlib
        _tmp_path = os.path.abspath(os.path.join(tempfile.gettempdir(), str(id(self))))

        if not os.path.exists(_tmp_path):
            os.makedirs(_tmp_path)
        return _tmp_path

    def _init_doc(self, add_date=True):
        doc = pyl.Document(geometry_options=geometry_options)
        doc.packages.append(pyl.Package("float"))
        doc.packages.append(pyl.Package("hyperref"))

        doc.preamble.append(pyl.Command("title", self.title))
        if add_date:
            doc.preamble.append(pyl.Command("date", pyl.NoEscape(r"\today")))
        else:
            doc.preamble.append(pyl.Command("date", pyl.NoEscape(r"")))
        doc.append(pyl.NoEscape(r"\maketitle"))
        doc.append(pyl.NewPage())
        doc.append(pyl.Command("tableofcontents"))
        doc.append(pyl.NewPage())

        return doc

    # def _save_fig(self, fig, *args, extension='pdf', **kwargs):
    #     filename = '{}.{}'.format(str(uuid.uuid4()), extension.strip('.'))
    #     filepath = os.path.join(self._temp_dir, filename)
    #     fig.savefig(filepath, *args, **kwargs)
    #     return filepath

    def reset_doc(self, add_date=True):
        self.doc = self._init_doc(add_date=add_date)

    def add_standard_figure(self, name, **kwargs):
        extension = ".pdf"
        self.tex_dict[name] = {}

        module = import_module("pyhdx.plot")
        f = getattr(module, name)
        arg_dict = self._get_arg(name)
        width = kwargs.pop("width", PAGE_WIDTH)

        for args_name, arg in arg_dict.items():
            fig_func = partial(
                f, arg, width=width, **kwargs
            )  # todo perhaps something like fig = lazy(func(args, **kwargs))?
            file_name = "{}.{}".format(str(uuid.uuid4()), extension.strip("."))
            file_path = self._temp_dir / file_name

            self.figure_queue.append((file_path, fig_func))

            tex_func = partial(_place_figure, file_path)
            self.tex_dict[name][args_name] = [tex_func]

    # def _get_args(self, plot_func_name):
    #     #Add _figure suffix if not present
    #     if not plot_func_name.endswith('_figure'):
    #         plot_func_name += '_figure'
    #
    #     if plot_func_name == 'peptide_coverage_figure':
    #         return {hdxm.name: [hdxm.data] for hdxm in self.fit_result.hdxm_set.hdxm_list}
    #     elif plot_func_name == 'residue_time_scatter_figure':
    #         return {hdxm.name: [hdxm] for hdxm in self.fit_result.hdxm_set.hdxm_list}
    #     elif plot_func_name == 'residue_scatter_figure':
    #         return {'All states': [self.fit_result.hdxm_set]}
    #     elif plot_func_name == 'dG_scatter_figure':
    #         return {'All states': [self.fit_result.output]}
    #     elif plot_func_name == 'ddG_scatter_figure':
    #         return {'All states': [self.fit_result.output.df]}  # Todo change protein object to dataframe!
    #     elif plot_func_name == 'linear_bars_figure':
    #         return {'All states': [self.fit_result.output.df]}
    #     elif plot_func_name == 'rainbowclouds_figure':
    #         return {'All states': [self.fit_result.output.df]}
    #     else:
    #         raise ValueError(f"Unknown plot function {plot_func_name!r}")

    def add_peptide_uptake_curves(self, layout=(5, 4), time_axis=None):
        extension = ".pdf"
        self.tex_dict["peptide_uptake"] = {}

        nrows, ncols = layout
        n = nrows * ncols
        time = time_axis or self.get_fit_timepoints()
        if time.ndim == 1:
            time = np.tile(
                time, (len(self.fit_result), 1)
            )  # todo move shape change to FitResult object

        d_calc = self.fit_result(time)  # Ns x Np x Nt

        fig_factory = partial(
            pplt.subplots,
            ncols=ncols,
            nrows=nrows,
            sharex=1,
            sharey=1,
            width=f"{PAGE_WIDTH}mm",
        )

        # iterate over samples
        for hdxm, d_calc_s in zip(self.fit_result.hdxm_set, d_calc):
            name = hdxm.name
            indices = range(hdxm.Np)
            chunks = [indices[i : i + n] for i in range(0, len(indices), n)]

            tex = []
            for chunk in chunks:
                file_name = "{}.{}".format(str(uuid.uuid4()), extension.strip("."))
                file_path = self._temp_dir / file_name

                fig_func = partial(
                    _peptide_uptake_figure, fig_factory, chunk, time[0], d_calc_s, hdxm
                )
                self.figure_queue.append((file_path, fig_func))

                tex_func = partial(_place_figure, file_path)
                tex.append(tex_func)

            self.tex_dict["peptide_uptake"][name] = tex

    def generate_latex(
        self, sort_by="graphs"
    ):  # graphs = []  #todo allow for setting which graphs to output
        if sort_by == "graphs":
            for graph_type, state_dict in self.tex_dict.items():
                # todo map graph type to human readable section name
                with self.doc.create(pyl.Section(graph_type)):
                    for state, tex_list in state_dict.items():
                        with self.doc.create(pyl.Subsection(state)):
                            [tex_func(doc=self.doc) for tex_func in tex_list]
        else:
            raise NotImplementedError("Sorting by protein state not implemented")

    def generate_figures(self, executor="local"):
        if isinstance(executor, futures.Executor):
            exec_klass = executor
        elif executor == "process":
            exec_klass = futures.ProcessPoolExecutor()
        elif executor == "local":
            exec_klass = LocalThreadExecutor()
        else:
            raise ValueError("Invalid value for 'executor'")

        total = len(self.figure_queue)
        ft = [exec_klass.submit(run, item) for item in self.figure_queue]
        with tqdm(total=total, desc="Generating figures") as pbar:
            for future in futures.as_completed(ft):
                pbar.update(1)

    def generate_pdf(self, file_path, cleanup=True, **kwargs):
        defaults = {"compiler_args": ["--xelatex"]}
        defaults.update(kwargs)

        self.doc.generate_pdf(file_path, **defaults)