Skip to content

Commit

Permalink
Started with moving reports
Browse files Browse the repository at this point in the history
  • Loading branch information
Lunga001 committed Nov 20, 2023
1 parent be03f03 commit 6c3980c
Show file tree
Hide file tree
Showing 48 changed files with 5,656 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/bika/reports/browser/reports/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Adding new reports
==================

Edit one of the report selection categories to add your report, and the form
that will be displayed to select criteria. This file is currently one of:

bika/lims/browser/reports/templates/administration.pt
bika/lims/browser/reports/templates/productivity.pt
bika/lims/browser/reports/templates/qualitycontrol.pt

Add the following files for your report:

bika/lims/browser/reports/templates/your_report_name.pt
bika/lims/browser/reports/your_reort_name.py

Report names should be in the same form as the existing ones:
category_reportname.

It is assumed that report_name.py contains a class called Report.

The Report class should return a dictionary of 'report_title' and
'report_data'

In case of an error, the Report class may return a string. This is assumed
to be a template output, and is rendered.

313 changes: 313 additions & 0 deletions src/bika/reports/browser/reports/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
# -*- coding: utf-8 -*-
#
# This file is part of SENAITE.CORE.
#
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright 2018-2021 by it's authors.
# Some rights reserved, see README and LICENSE.

import importlib
import os
from datetime import datetime

from bika.lims import api
from bika.lims import bikaMessageFactory as _
from bika.lims.browser import BrowserView
from bika.lims.browser.bika_listing import BikaListingView
from bika.reports.browser.reports.selection_macros import SelectionMacrosView
from bika.reports.interfaces import IAdministrationReport
from bika.reports.interfaces import IProductivityReport
from bika.lims.utils import createPdf
from bika.lims.utils import getUsers
from bika.lims.utils import logged_in_client
from DateTime import DateTime
from plone.app.layout.globals.interfaces import IViewView
from Products.CMFCore.utils import getToolByName
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from senaite.core.catalog import REPORT_CATALOG
from zope.component import getAdapters
from zope.interface import implements


class ProductivityView(BrowserView):
""" Productivity View form
"""
implements(IViewView)
template = ViewPageTemplateFile("templates/productivity.pt")

def __init__(self, context, request):
BrowserView.__init__(self, context, request)
self.context = context
self.request = request

def __call__(self):
self.selection_macros = SelectionMacrosView(self.context, self.request)
self.icon = self.portal_url + "/++resource++bika.lims.images/report_big.png"
self.getAnalysts = getUsers(self.context,
['Manager', 'LabManager', 'Analyst'])

self.additional_reports = []
adapters = getAdapters((self.context, ), IProductivityReport)
for name, adapter in adapters:
report_dict = adapter(self.context, self.request)
report_dict['id'] = name
self.additional_reports.append(report_dict)

return self.template()


class AdministrationView(BrowserView):
""" Administration View form
"""
implements(IViewView)
template = ViewPageTemplateFile("templates/administration.pt")

def __init__(self, context, request):
BrowserView.__init__(self, context, request)
self.context = context
self.request = request

def __call__(self):
self.selection_macros = SelectionMacrosView(self.context, self.request)
self.icon = self.portal_url + "/++resource++bika.lims.images/report_big.png"

self.additional_reports = []
adapters = getAdapters((self.context, ), IAdministrationReport)
for name, adapter in adapters:
report_dict = adapter(self.context, self.request)
report_dict['id'] = name
self.additional_reports.append(report_dict)

return self.template()


class ReportHistoryView(BikaListingView):
""" Report history form
"""
implements(IViewView)

def __init__(self, context, request):
super(ReportHistoryView, self).__init__(context, request)

self.catalog = REPORT_CATALOG

self.context_actions = {}

self.show_select_row = False
self.show_select_column = False
self.show_column_toggles = False
self.show_workflow_action_buttons = False
self.show_select_all_checkbox = False
self.pagesize = 50

self.icon = self.portal_url + "/++resource++bika.lims.images/report_big.png"
self.title = self.context.translate(_("Reports"))
self.description = ""

# this is set up in call where member is authenticated
self.columns = {}
self.review_states = []

def __call__(self):
self.columns = {
'Title': {
'title': _('Title'),
'attr': 'Title',
'index': 'title', },
'file_size': {
'title': _("Size"),
'attr': 'getFileSize',
'sortable': False, },
'created': {
'title': _("Created"),
'attr': 'created',
'index': 'created', },
'creator': {
'title': _("By"),
'attr': 'getCreatorFullName',
'index': 'Creator', }, }
self.review_states = [
{'id': 'default',
'title': 'All',
'contentFilter': {},
'columns': ['Title',
'file_size',
'created',
'creator']},
]

self.contentFilter = {
'portal_type': 'Report',
'sort_order': 'reverse'}

this_client = logged_in_client(self.context)
if this_client:
self.contentFilter['getClientUID'] = this_client.UID()
else:
self.columns['client'] = {
'title': _('Client'),
'attr': 'getClientTitle',
'replace_url': 'getClientURL', }

return super(ReportHistoryView, self).__call__()

def lookupMime(self, name):
mimetool = getToolByName(self, 'mimetypes_registry')
mimetypes = mimetool.lookup(name)
if len(mimetypes):
return mimetypes[0].name()
else:
return name

def folderitem(self, obj, item, index):
item = BikaListingView.folderitem(self, obj, item, index)
# https://github.com/collective/uwosh.pfg.d2c/issues/20
# https://github.com/collective/uwosh.pfg.d2c/pull/21
item['replace']['Title'] = \
"<a href='%s/ReportFile'>%s</a>" % \
(item['url'], item['Title'])
item['replace']['created'] = self.ulocalized_time(item['created'])
return item


class SubmitForm(BrowserView):
""" Redirect to specific report
"""
implements(IViewView)
frame_template = ViewPageTemplateFile("templates/report_frame.pt")
# default and errors use this template:
template = ViewPageTemplateFile("templates/productivity.pt")

def __init__(self, context, request):
BrowserView.__init__(self, context, request)
self.context = context
self.request = request

def __call__(self):
"""Create and render selected report
"""

# if there's an error, we return productivity.pt which requires these.
self.selection_macros = SelectionMacrosView(self.context, self.request)
self.additional_reports = []
adapters = getAdapters((self.context, ), IProductivityReport)
for name, adapter in adapters:
report_dict = adapter(self.context, self.request)
report_dict['id'] = name
self.additional_reports.append(report_dict)

report_id = self.request.get('report_id', '')
if not report_id:
message = _("No report specified in request")
self.logger.error(message)
self.context.plone_utils.addPortalMessage(message, 'error')
return self.template()

self.date = DateTime()
username = self.context.portal_membership.getAuthenticatedMember().getUserName()
self.reporter = self.user_fullname(username)
self.reporter_email = self.user_email(username)

# signature image
self.reporter_signature = ""
c = [x for x in self.senaite_catalog_setup(portal_type='LabContact')
if x.getObject().getUsername() == username]
if c:
sf = c[0].getObject().getSignature()
if sf:
self.reporter_signature = sf.absolute_url() + "/Signature"

lab = self.context.bika_setup.laboratory
self.laboratory = lab
self.lab_title = lab.getName()
self.lab_address = lab.getPrintAddress()
self.lab_email = lab.getEmailAddress()
self.lab_url = lab.getLabURL()

client = logged_in_client(self.context)
if client:
clientuid = client.UID()
self.client_title = client.Title()
self.client_address = client.getPrintAddress()
else:
clientuid = None
self.client_title = None
self.client_address = None

# Render form output

# the report can add file names to this list; they will be deleted
# once the PDF has been generated. temporary plot image files, etc.
self.request['to_remove'] = []

if "report_module" in self.request:
module = self.request["report_module"]
else:
module = "bika.lims.browser.reports.%s" % report_id
try:
Report = getattr(importlib.import_module(module), "Report")
# required during error redirect: the report must have a copy of
# additional_reports, because it is used as a surrogate view.
Report.additional_reports = self.additional_reports
except (ImportError, AttributeError):
message = "Report %s.Report not found (shouldn't happen)" % module
self.logger.error(message)
self.context.plone_utils.addPortalMessage(message, 'error')
return self.template()

# Report must return dict with:
# - report_title - title string for pdf/history listing
# - report_data - rendered report
output = Report(self.context, self.request)()

# if CSV output is chosen, report returns None
if not output:
return

if type(output) in (str, unicode, bytes):
# remove temporary files
for f in self.request['to_remove']:
os.remove(f)
return output

# The report output gets pulled through report_frame.pt
self.reportout = output['report_data']
framed_output = self.frame_template()

# this is the good part
pdf = createPdf(framed_output)

# remove temporary files
for f in self.request['to_remove']:
os.remove(f)

if not pdf:
return

# Create new report object
title = output["report_title"]
data = {"title": title, "Client": clientuid, "ReportFile": pdf}
api.create(self.context, "Report", **data)

now = datetime.now().strftime("%y%m%d%H%M%S")
fn = "{}-{}.pdf".format(now, title)

setheader = self.request.response.setHeader
setheader("Content-Type", "application/pdf")
setheader("Content-Disposition", "attachment; filename=\"%s\"" % fn)
setheader("Content-Length", len(pdf))
setheader("Cache-Control", "no-store")
setheader("Pragma", "no-cache")
self.request.response.write(pdf)
Loading

0 comments on commit 6c3980c

Please sign in to comment.