Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ContentSectionViewletManager to allow dynamic addition of sections #1690

Merged
merged 10 commits into from
Dec 3, 2020
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
2.0.0rc3 (unreleased)
---------------------

- #1690 Allow to add new sections in Sample via adapters
- #1689 Display tabs in content edit view when more than one group
- #1682 Fix `LocationError` when editing a entry in the configuration registry
- #1685 Remove Supply Orders
Expand Down
18 changes: 18 additions & 0 deletions src/bika/lims/browser/analysisrequest/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@
layer="bika.lims.interfaces.IBikaLIMS"
/>

<!-- Section for Field Analyses -->
<subscriber
for="bika.lims.interfaces.IAnalysisRequest"
provides="senaite.core.interfaces.ISampleSection"
factory=".sections.FieldAnalysesSection"/>

<!-- Section for Laboratory Analyses -->
<subscriber
for="bika.lims.interfaces.IAnalysisRequest"
provides="senaite.core.interfaces.ISampleSection"
factory=".sections.LabAnalysesSection"/>

<!-- Section for QC Analyses -->
<subscriber
for="bika.lims.interfaces.IAnalysisRequest"
provides="senaite.core.interfaces.ISampleSection"
factory=".sections.QCAnalysesSection"/>

<!-- Table Lab Analyses -->
<browser:page
for="bika.lims.interfaces.IAnalysisRequest"
Expand Down
84 changes: 84 additions & 0 deletions src/bika/lims/browser/analysisrequest/sections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# -*- 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-2020 by it's authors.
# Some rights reserved, see README and LICENSE.

from bika.lims import api
from bika.lims import senaiteMessageFactory as _
from bika.lims.interfaces import IAnalysisRequest
from senaite.core.interfaces import ISampleSection
from zope.component import adapts
from zope.interface import implementer


@implementer(ISampleSection)
class LabAnalysesSection(object):
"""Lab Analyses section adapter for Sample view
"""
adapts(IAnalysisRequest)

# Order index for this section
order = 10

title = _("Analyses")
icon_name = "analysisservice"
capture = "lab"

def __init__(self, sample):
self.sample = sample

def is_visible(self):
"""Returns true if this sample contains at least one analysis for the
point of capture (capture)
"""
analyses = self.sample.getAnalyses(getPointOfCapture=self.capture)
return len(analyses) > 0

def get_listing_view(self):
request = api.get_request()
view_name = "table_{}_analyses".format(self.capture)
view = api.get_view(view_name, context=self.sample, request=request)
return view

def render(self):
view = self.get_listing_view()
view.update()
view.before_render()
return view.ajax_contents_table()


class FieldAnalysesSection(LabAnalysesSection):
"""Field analyses section adapter for Sample view
"""
order = 5
title = _("Field Analyses")
capture = "field"


class QCAnalysesSection(LabAnalysesSection):
"""QC analyses section adapter for Sample view
"""
order = 50
title = _("QC Analyses")
capture = "qc"

def is_visible(self):
"""Returns true if this sample contains at least one qc analysis
"""
analyses = self.sample.getQCAnalyses()
return len(analyses) > 0
Original file line number Diff line number Diff line change
Expand Up @@ -70,47 +70,20 @@
</div>
<!-- /Remarks Widget -->

<tal:field_analyses condition="python:view.has_field_analyses()">
<div class="row">
<div class="col-sm-12">
<h3>
<i class="fieldanalyses-icon" title="Field Analyses" i18n:attributes="title">
<svg tal:replace="structure senaite_theme/icon_data/analysisservice" />
</i>
<span i18n:translate="">Field Analyses</span>
</h3>
<span tal:replace="structure python:view.render_analyses_table(table='field')"/>
</div>
</div>
</tal:field_analyses>

<tal:lab_analyses condition="python:view.has_lab_analyses()">
<div class="row">
<div class="col-sm-12">
<h3>
<i class="labanalyses-icon" title="Lab Analyses" i18n:attributes="title">
<svg tal:replace="structure senaite_theme/icon_data/analysisservice" />
</i>
<span i18n:translate="">Lab Analyses</span>
</h3>
<span tal:replace="structure python:view.render_analyses_table(table='lab')"/>
</div>
</div>
</tal:lab_analyses>

<tal:qc_analyses condition="python:view.has_qc_analyses()">
<div class="row">
<div class="col-sm-12">
<h3>
<i class="qcanalyses-icon" title="QC Analyses" i18n:attributes="title">
<svg tal:replace="structure senaite_theme/icon_data/analysisservice" />
</i>
<span i18n:translate="">QC Analyses</span>
</h3>
<span tal:replace="structure python:view.render_analyses_table(table='qc')"/>
</div>
<!-- Additional sections -->
<div class="row mb-4" tal:repeat="section view/get_sections">
<div class="col-sm-12"
tal:condition="section/is_visible">
<h3 tal:define="icon section/icon_name|nothing;
title section/title|nothing"
tal:condition="title">
<img tal:condition="icon|nothing"
tal:attributes="src string:senaite_theme/icon/${icon}"/>
<span i18n:translate="" tal:content="title"/>
</h3>
<span tal:replace="structure python:section.render()"/>
</div>
</tal:qc_analyses>
</div>

<!-- Results interpretation -->
<div class="row">
Expand Down
41 changes: 9 additions & 32 deletions src/bika/lims/browser/analysisrequest/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
from bika.lims.browser import BrowserView
from bika.lims.browser.header_table import HeaderTableView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from senaite.core.interfaces import ISampleSection
from zope.component import subscribers

from resultsinterpretation import ARResultsInterpretationView


Expand All @@ -47,39 +50,13 @@ def __call__(self):

return self.template()

def render_analyses_table(self, table="lab"):
"""Render Analyses Table
"""
if table not in ["lab", "field", "qc"]:
raise KeyError("Table '{}' does not exist".format(table))
view_name = "table_{}_analyses".format(table)
view = api.get_view(
view_name, context=self.context, request=self.request)
# Call listing hooks
view.update()
view.before_render()
return view.ajax_contents_table()

def has_lab_analyses(self):
"""Check if the AR contains lab analyses
"""
# Negative performance impact - add a Metadata column
analyses = self.context.getAnalyses(getPointOfCapture="lab")
return len(analyses) > 0

def has_field_analyses(self):
"""Check if the AR contains field analyses
"""
# Negative performance impact - add a Metadata column
analyses = self.context.getAnalyses(getPointOfCapture="field")
return len(analyses) > 0

def has_qc_analyses(self):
"""Check if the AR contains field analyses
def get_sections(self):
"""Returns a list with adapters that implement ISampleSection
"""
# Negative performance impact - add a Metadata column
analyses = self.context.getQCAnalyses()
return len(analyses) > 0
# We use subscriber adapters here because we need different add-ons to
# be able to add their own sections without dependencies amongst them
sections = subscribers((self.context, ), ISampleSection)
return sorted(sections, key=lambda s: getattr(s, "order", 100))

def is_hazardous(self):
"""Checks if the AR is hazardous
Expand Down
13 changes: 13 additions & 0 deletions src/senaite/core/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,16 @@ class IHideActionsMenu(Interface):
"""Marker interface that can be applied for conttents that should not
display the content actions menu
"""


class ISampleSection(Interface):
"""Marker for additional sections to be displayed in Sample view/edit form
"""

def is_visible(self):
"""Returns whether this section is visible or not
"""

def render(self):
"""Returns the html-like section
"""