# This file is part of lims_report_html module for Tryton. # The COPYRIGHT file at the top level of this repository contains # the full copyright notices and license terms. import os from mimetypes import guess_type as mime_guess_type from binascii import b2a_base64 from functools import partial from decimal import Decimal from datetime import date, datetime from lxml import html as lxml_html from base64 import b64encode from babel.support import Translations as BabelTranslations from jinja2 import contextfilter, Markup from jinja2 import Environment, FunctionLoader from io import BytesIO from PyPDF2 import PdfFileMerger from PyPDF2.utils import PdfReadError from trytond.model import ModelView, ModelSQL, fields from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval from trytond.transaction import Transaction from trytond.exceptions import UserError from trytond.i18n import gettext from trytond.tools import file_open from .generator import PdfGenerator class ResultsReportVersionDetail(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail' template = fields.Many2One('lims.result_report.template', 'Report Template', domain=[('type', 'in', [None, 'base'])], states={'readonly': Eval('state') != 'draft'}, depends=['state']) sections = fields.One2Many('lims.results_report.version.detail.section', 'version_detail', 'Sections') previous_sections = fields.Function(fields.One2Many( 'lims.results_report.version.detail.section', 'version_detail', 'Previous Sections', domain=[('position', '=', 'previous')]), 'get_previous_sections', setter='set_previous_sections') following_sections = fields.Function(fields.One2Many( 'lims.results_report.version.detail.section', 'version_detail', 'Following Sections', domain=[('position', '=', 'following')]), 'get_following_sections', setter='set_following_sections') trend_charts = fields.One2Many( 'lims.results_report.version.detail.trend.chart', 'version_detail', 'Trend Charts') charts_x_row = fields.Selection([ ('1', '1'), ('2', '2'), ], 'Charts per Row') @classmethod def __setup__(cls): super().__setup__() if 'invisible' in cls.resultrange_origin.states: del cls.resultrange_origin.states['invisible'] if 'required' in cls.resultrange_origin.states: del cls.resultrange_origin.states['required'] @staticmethod def default_charts_x_row(): return '1' @fields.depends('template', '_parent_template.trend_charts', '_parent_template.sections') def on_change_template(self): if self.template and self.template.trend_charts: self.trend_charts = [{ 'chart': c.chart.id, 'order': c.order, } for c in self.template.trend_charts] self.charts_x_row = self.template.charts_x_row if self.template and self.template.sections: self.sections = [{ 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } for s in self.template.sections] def get_previous_sections(self, name): return [s.id for s in self.sections if s.position == 'previous'] @classmethod def set_previous_sections(cls, sections, name, value): if not value: return cls.write(sections, {'sections': value}) def get_following_sections(self, name): return [s.id for s in self.sections if s.position == 'following'] @classmethod def set_following_sections(cls, sections, name, value): if not value: return cls.write(sections, {'sections': value}) @classmethod def _get_fields_from_samples(cls, samples): Notebook = Pool().get('lims.notebook') detail_default = super()._get_fields_from_samples(samples) for sample in samples: notebook = Notebook(sample['notebook']) result_template = notebook.fraction.sample.result_template if result_template: detail_default['template'] = result_template.id if result_template.trend_charts: detail_default['trend_charts'] = [('create', [{ 'chart': c.chart.id, 'order': c.order, } for c in result_template.trend_charts])] detail_default['charts_x_row'] = ( result_template.charts_x_row) if result_template.sections: detail_default['sections'] = [('create', [{ 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } for s in result_template.sections])] resultrange_origin = notebook.fraction.sample.resultrange_origin if resultrange_origin: detail_default['resultrange_origin'] = resultrange_origin.id return detail_default @classmethod def _get_fields_from_detail(cls, detail): detail_default = super()._get_fields_from_detail(detail) if detail.template: detail_default['template'] = detail.template.id if detail.trend_charts: detail_default['trend_charts'] = [('create', [{ 'chart': c.chart.id, 'order': c.order, } for c in detail.trend_charts])] detail_default['charts_x_row'] = detail.charts_x_row if detail.sections: detail_default['sections'] = [('create', [{ 'name': s.name, 'data': s.data, 'data_id': s.data_id, 'position': s.position, 'order': s.order, } for s in detail.sections])] return detail_default class ResultsReportVersionDetailSection(ModelSQL, ModelView): 'Results Report Version Detail Section' __name__ = 'lims.results_report.version.detail.section' _order_name = 'order' version_detail = fields.Many2One('lims.results_report.version.detail', 'Report Detail', ondelete='CASCADE', select=True, required=True) name = fields.Char('Name', required=True) data = fields.Binary('File', filename='name', required=True, file_id='data_id', store_prefix='results_report_section') data_id = fields.Char('File ID', readonly=True) position = fields.Selection([ ('previous', 'Previous'), ('following', 'Following'), ], 'Position', required=True) order = fields.Integer('Order') @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('order', 'ASC')) @classmethod def validate(cls, sections): super().validate(sections) merger = PdfFileMerger(strict=False) for section in sections: filedata = BytesIO(section.data) try: merger.append(filedata) except PdfReadError: raise UserError(gettext('lims_report_html.msg_section_pdf')) class ResultsReportVersionDetailTrendChart(ModelSQL, ModelView): 'Results Report Version Detail Trend Chart' __name__ = 'lims.results_report.version.detail.trend.chart' _order_name = 'order' version_detail = fields.Many2One('lims.results_report.version.detail', 'Report Detail', ondelete='CASCADE', select=True, required=True) chart = fields.Many2One('lims.trend.chart', 'Trend Chart', required=True, domain=[('active', '=', True)]) order = fields.Integer('Order') class ResultsReportVersionDetailSample(metaclass=PoolMeta): __name__ = 'lims.results_report.version.detail.sample' trend_charts = fields.Function(fields.Text('Trend Charts'), 'get_trend_charts') attachments = fields.Function(fields.Text('Attachments'), 'get_attachments') def get_trend_charts(self, name): pool = Pool() OpenTrendChart = pool.get('lims.trend.chart.open', type='wizard') ResultReport = pool.get('lims.result_report', type='report') if not self.version_detail.trend_charts: return '' charts = [] for tc in self.version_detail.trend_charts: session_id, _, _ = OpenTrendChart.create() open_chart = OpenTrendChart(session_id) open_chart.start.chart = tc.chart open_chart.start.notebook = self.notebook open_chart.transition_compute() plot = tc.chart.get_plot(session_id) charts.append(plot) div_row = '