From c628400515cf4f8c4d2262f428796d59ae7eb26c Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 13:53:42 +0100 Subject: [PATCH 01/28] Removed stale type registrations --- bika/lims/__init__.py | 3 - bika/lims/content/bikacache.py | 51 ------------ bika/lims/content/invoicebatch.py | 34 -------- bika/lims/content/invoicefolder.py | 34 -------- bika/lims/content/rejectanalysis.py | 50 ------------ bika/lims/interfaces/__init__.py | 10 --- bika/lims/profiles/default/propertiestool.xml | 2 - bika/lims/profiles/default/types.xml | 3 - .../lims/profiles/default/types/BikaCache.xml | 15 ---- .../profiles/default/types/InvoiceBatch.xml | 77 ------------------- .../profiles/default/types/InvoiceFolder.xml | 26 ------- bika/lims/upgrade/v01_03_003.py | 25 ++++++ 12 files changed, 25 insertions(+), 305 deletions(-) delete mode 100644 bika/lims/content/bikacache.py delete mode 100644 bika/lims/content/invoicebatch.py delete mode 100644 bika/lims/content/invoicefolder.py delete mode 100644 bika/lims/content/rejectanalysis.py delete mode 100644 bika/lims/profiles/default/types/BikaCache.xml delete mode 100644 bika/lims/profiles/default/types/InvoiceBatch.xml delete mode 100644 bika/lims/profiles/default/types/InvoiceFolder.xml diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index 1801d75700..e1f0d547d9 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -82,7 +82,6 @@ def initialize(context): from content.batch import Batch # noqa from content.batchfolder import BatchFolder # noqa from content.batchlabel import BatchLabel # noqa - from content.bikacache import BikaCache # noqa from content.bikaschema import BikaSchema # noqa from content.bikasetup import BikaSetup # noqa from content.calculation import Calculation # noqa @@ -103,8 +102,6 @@ def initialize(context): from content.instrumenttype import InstrumentType # noqa from content.instrumentvalidation import InstrumentValidation # noqa from content.invoice import Invoice # noqa - from content.invoicebatch import InvoiceBatch # noqa - from content.invoicefolder import InvoiceFolder # noqa from content.labcontact import LabContact # noqa from content.laboratory import Laboratory # noqa from content.labproduct import LabProduct # noqa diff --git a/bika/lims/content/bikacache.py b/bika/lims/content/bikacache.py deleted file mode 100644 index 21114b8a6b..0000000000 --- a/bika/lims/content/bikacache.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- 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 zope.interface import implements -from Products.Archetypes import atapi -from Products.Archetypes.public import BaseContent -from bika.lims.content.bikaschema import BikaSchema -from bika.lims import bikaMessageFactory as _ -from bika.lims import config - - -schema = BikaSchema.copy() + atapi.Schema(( - #'Key' field is name of the Cache object, must be Unique - atapi.StringField('Key',default=''), - - # 'Value' is ID of the last created object. Must be inscreased before using. - atapi.StringField('Value',default='') - -)) - -schema['title'].widget.visible = False - -class BikaCache(BaseContent): - """ - BikaCache objects stores information about 'Last Created ID's of different - types. For each object type, there must be only one Cache object, and the ID - of its Last Created Object. - It is used to avoid querying whole catalog just to get the highest ID for any - kind of object. - """ - schema = schema - -# Activating the content type in Archetypes' internal types registry -atapi.registerType(BikaCache, config.PROJECTNAME) diff --git a/bika/lims/content/invoicebatch.py b/bika/lims/content/invoicebatch.py deleted file mode 100644 index 1302ed4b6d..0000000000 --- a/bika/lims/content/invoicebatch.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- 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.config import PROJECTNAME -from bika.lims.interfaces import IInvoiceBatch -from Products.Archetypes.public import registerType -from Products.Archetypes.public import BaseFolder -from zope.interface import implements - - -class InvoiceBatch(BaseFolder): - """REMOVE AFTER 1.3 - """ - implements(IInvoiceBatch) - - -registerType(InvoiceBatch, PROJECTNAME) diff --git a/bika/lims/content/invoicefolder.py b/bika/lims/content/invoicefolder.py deleted file mode 100644 index a24a33c925..0000000000 --- a/bika/lims/content/invoicefolder.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- 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.config import PROJECTNAME -from bika.lims.interfaces import IInvoiceFolder -from Products.Archetypes import atapi -from Products.ATContentTypes.content import folder -from zope.interface import implements - - -class InvoiceFolder(folder.ATFolder): - """REMOVE AFTER 1.3 - """ - implements(IInvoiceFolder) - - -atapi.registerType(InvoiceFolder, PROJECTNAME) diff --git a/bika/lims/content/rejectanalysis.py b/bika/lims/content/rejectanalysis.py deleted file mode 100644 index 0cd0a90ae7..0000000000 --- a/bika/lims/content/rejectanalysis.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- 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 AccessControl import ClassSecurityInfo -from Products.Archetypes.public import ReferenceField, Schema, registerType -from bika.lims.content.analysis import Analysis -from bika.lims.config import PROJECTNAME -from bika.lims.content.analysis import schema as analysis_schema -from bika.lims.interfaces import IRejectAnalysis -from zope.interface import implements - -schema = analysis_schema + Schema(( - # The analysis that was originally rejected - ReferenceField( - 'Analysis', - allowed_types=('Analysis',), - relationship='RejectAnalysisAnalysis', - ), -)) - - -class RejectAnalysis(Analysis): - implements(IRejectAnalysis) - security = ClassSecurityInfo() - - schema = schema - - @security.public - def getSample(self): - return self.aq_parent - - -registerType(RejectAnalysis, PROJECTNAME) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 78cb9e5ad0..0d989ae38e 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -270,16 +270,6 @@ class IInvoice(Interface): """ -class IInvoiceBatch(Interface): - """Invoice Batch - """ - - -class IInvoiceFolder(Interface): - """Invoices Folder - """ - - class IBikaSetup(Interface): """Marker interface for the LIMS Setup """ diff --git a/bika/lims/profiles/default/propertiestool.xml b/bika/lims/profiles/default/propertiestool.xml index b687c799e4..53489e76d5 100644 --- a/bika/lims/profiles/default/propertiestool.xml +++ b/bika/lims/profiles/default/propertiestool.xml @@ -60,7 +60,6 @@ - @@ -104,7 +103,6 @@ - False diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index 2c3121765f..cae6bde25e 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -24,7 +24,6 @@ - @@ -50,8 +49,6 @@ - - diff --git a/bika/lims/profiles/default/types/BikaCache.xml b/bika/lims/profiles/default/types/BikaCache.xml deleted file mode 100644 index ad260a6042..0000000000 --- a/bika/lims/profiles/default/types/BikaCache.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - BikaCache - A contenttype to keep some necessary variables - BikaCache - bika.lims - addBikaCache - True - True - True - - diff --git a/bika/lims/profiles/default/types/InvoiceBatch.xml b/bika/lims/profiles/default/types/InvoiceBatch.xml deleted file mode 100644 index 7075f659a7..0000000000 --- a/bika/lims/profiles/default/types/InvoiceBatch.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - ++resource++bika.lims.images/invoice.png - InvoiceBatch - bika.lims - addInvoiceBatch - - - False - True - - - - False - False - base_view - - - - - - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/types/InvoiceFolder.xml b/bika/lims/profiles/default/types/InvoiceFolder.xml deleted file mode 100644 index 0aacd64f90..0000000000 --- a/bika/lims/profiles/default/types/InvoiceFolder.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - Statements - - ++resource++bika.lims.images/invoice.png - InvoiceFolder - bika.lims - addInvoiceFolder - - - False - True - - - - False - False - - - - - diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index dd6d67bd74..c6daa96d1c 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -42,6 +42,14 @@ profile = "profile-{0}:default".format(product) +TYPES_TO_REMOVE = [ + "BikaCache", + "RejectAnalysis", + # invoices were removed in upgrade step 1.3.0 + "InvoiceBatch", + "InvoiceFolder", +] + JAVASCRIPTS_TO_REMOVE = [ # moved from lims -> core "++resource++senaite.lims.jquery.js/jquery-2.2.4.min.js", @@ -350,6 +358,9 @@ def upgrade(tool): remove_stale_css(portal) remove_stale_javascripts(portal) + # remove stale type regsitrations + remove_stale_type_registrations(portal) + logger.info("{0} upgraded to version {1}".format(product, version)) return True @@ -756,3 +767,17 @@ def update_wf_received_samples(portal): sample.reindexObject() logger.info("Updating workflow mappings for received samples [DONE]") + + +def remove_stale_type_registrations(portal): + """Remove stale contents from the portal_types + """ + logger.info("Removing stale type registrations ...") + + pt = portal.portal_types + for t in TYPES_TO_REMOVE: + logger.info("Removing type registrations for '{}'".format(t)) + if t in pt.objectIds(): + pt.manage_delObjects(t) + + logger.info("Removing stale type registrations [DONE]") From b516713d585fc03814389369b54db5488cb4cbbe Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 14:03:09 +0100 Subject: [PATCH 02/28] removed initialization of rejectanalysis --- bika/lims/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index e1f0d547d9..7075356cfb 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -119,7 +119,6 @@ def initialize(context): from content.referencesample import ReferenceSample # noqa from content.referencesamplesfolder import ReferenceSamplesFolder # noqa from content.reflexrule import ReflexRule # noqa - from content.rejectanalysis import RejectAnalysis # noqa from content.report import Report # noqa from content.reportfolder import ReportFolder # noqa from content.sample import Sample # noqa From ddbd84de5d76c1e4e447bb68dced8e90e23e7948 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 14:51:19 +0100 Subject: [PATCH 03/28] removed invoice and invoicebatch from factorytool --- bika/lims/profiles/default/factorytool.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/bika/lims/profiles/default/factorytool.xml b/bika/lims/profiles/default/factorytool.xml index fe96653ce9..05b631bd10 100644 --- a/bika/lims/profiles/default/factorytool.xml +++ b/bika/lims/profiles/default/factorytool.xml @@ -31,8 +31,6 @@ - - From dfaa8177b1bdc173923856628f4787f88a15c9d6 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 15:32:04 +0100 Subject: [PATCH 04/28] Remove sampling rounds from core --- bika/lims/__init__.py | 2 - bika/lims/adapters/widgetvisibility.py | 2 +- bika/lims/browser/analysisrequest/add2.py | 3 - bika/lims/browser/client/__init__.py | 2 - bika/lims/browser/client/configure.zcml | 16 - .../browser/client/views/samplingrounds.py | 47 -- bika/lims/browser/client/views/srtemplates.py | 30 -- bika/lims/browser/configure.zcml | 2 - bika/lims/browser/js/bika.lims.loader.js | 8 - .../js/bika.lims.samplinground.print.js | 379 --------------- .../browser/js/bika.lims.samplingrounds.js | 116 ----- bika/lims/browser/samplinground/__init__.py | 19 - bika/lims/browser/samplinground/add.py | 33 -- .../browser/samplinground/analysisrequests.py | 267 ---------- .../lims/browser/samplinground/configure.zcml | 37 -- bika/lims/browser/samplinground/edit.py | 33 -- bika/lims/browser/samplinground/printform.py | 265 ---------- .../templates/print/default_form.css | 60 --- .../templates/print/default_form.pt | 134 ------ .../samplinground/templates/print_form.pt | 209 -------- bika/lims/browser/srtemplate/__init__.py | 19 - bika/lims/browser/srtemplate/artemplates.py | 211 -------- bika/lims/browser/srtemplate/configure.zcml | 14 - bika/lims/browser/widgets/__init__.py | 1 - .../widgets/srtemplateartemplateswidget.py | 121 ----- bika/lims/content/analysisrequest.py | 38 -- bika/lims/content/configure.zcml | 2 - bika/lims/content/samplinground.py | 454 ------------------ bika/lims/content/srtemplate.py | 140 ------ bika/lims/controlpanel/bika_samplingrounds.py | 116 ----- bika/lims/controlpanel/bika_srtemplates.py | 121 ----- bika/lims/controlpanel/configure.zcml | 16 - bika/lims/exportimport/setupdata/__init__.py | 2 +- bika/lims/interfaces/__init__.py | 10 - bika/lims/permissions.py | 7 - bika/lims/permissions.zcml | 7 - bika/lims/profiles/default/catalog.xml | 4 - bika/lims/profiles/default/controlpanel.xml | 18 - bika/lims/profiles/default/factorytool.xml | 1 - bika/lims/profiles/default/jsregistry.xml | 22 - bika/lims/profiles/default/propertiestool.xml | 2 - bika/lims/profiles/default/rolemap.xml | 14 - .../lims/profiles/default/structure/.preserve | 1 - .../default/structure/bika_setup/.objects | 2 - .../default/structure/bika_setup/.preserve | 2 - .../bika_setup/bika_samplingrounds/.object | 0 .../bika_samplingrounds/.properties | 4 - .../bika_setup/bika_srtemplates/.objects | 0 .../bika_setup/bika_srtemplates/.properties | 3 - bika/lims/profiles/default/types.xml | 4 - bika/lims/profiles/default/types/Client.xml | 26 - .../profiles/default/types/SRTemplate.xml | 59 --- .../profiles/default/types/SRTemplates.xml | 28 -- .../profiles/default/types/SamplingRound.xml | 93 ---- .../profiles/default/types/SamplingRounds.xml | 53 -- bika/lims/profiles/default/workflows.xml | 13 - .../definition.xml | 140 ------ bika/lims/setuphandlers.py | 1 - bika/lims/subscribers/configure.zcml | 6 - bika/lims/subscribers/samplinground.py | 35 -- bika/lims/upgrade/v01_03_003.py | 59 +++ 61 files changed, 61 insertions(+), 3472 deletions(-) delete mode 100644 bika/lims/browser/client/views/samplingrounds.py delete mode 100644 bika/lims/browser/client/views/srtemplates.py delete mode 100644 bika/lims/browser/js/bika.lims.samplinground.print.js delete mode 100644 bika/lims/browser/js/bika.lims.samplingrounds.js delete mode 100644 bika/lims/browser/samplinground/__init__.py delete mode 100644 bika/lims/browser/samplinground/add.py delete mode 100644 bika/lims/browser/samplinground/analysisrequests.py delete mode 100644 bika/lims/browser/samplinground/configure.zcml delete mode 100644 bika/lims/browser/samplinground/edit.py delete mode 100644 bika/lims/browser/samplinground/printform.py delete mode 100644 bika/lims/browser/samplinground/templates/print/default_form.css delete mode 100644 bika/lims/browser/samplinground/templates/print/default_form.pt delete mode 100644 bika/lims/browser/samplinground/templates/print_form.pt delete mode 100644 bika/lims/browser/srtemplate/__init__.py delete mode 100644 bika/lims/browser/srtemplate/artemplates.py delete mode 100644 bika/lims/browser/srtemplate/configure.zcml delete mode 100644 bika/lims/browser/widgets/srtemplateartemplateswidget.py delete mode 100644 bika/lims/content/samplinground.py delete mode 100644 bika/lims/content/srtemplate.py delete mode 100644 bika/lims/controlpanel/bika_samplingrounds.py delete mode 100644 bika/lims/controlpanel/bika_srtemplates.py delete mode 100644 bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.object delete mode 100644 bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.properties delete mode 100644 bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.objects delete mode 100644 bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.properties delete mode 100644 bika/lims/profiles/default/types/SRTemplate.xml delete mode 100644 bika/lims/profiles/default/types/SRTemplates.xml delete mode 100644 bika/lims/profiles/default/types/SamplingRound.xml delete mode 100644 bika/lims/profiles/default/types/SamplingRounds.xml delete mode 100644 bika/lims/profiles/default/workflows/bika_samplinground_workflow/definition.xml delete mode 100644 bika/lims/subscribers/samplinground.py diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index 7075356cfb..42943aa6f7 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -129,7 +129,6 @@ def initialize(context): from content.samplesfolder import SamplesFolder # noqa from content.sampletype import SampleType # noqa from content.samplingdeviation import SamplingDeviation # noqa - from content.srtemplate import SRTemplate # noqa from content.storagelocation import StorageLocation # noqa from content.subgroup import SubGroup # noqa from content.supplier import Supplier # noqa @@ -167,7 +166,6 @@ def initialize(context): from controlpanel.bika_samplepoints import SamplePoints # noqa from controlpanel.bika_sampletypes import SampleTypes # noqa from controlpanel.bika_samplingdeviations import SamplingDeviations # noqa - from controlpanel.bika_srtemplates import SRTemplates # noqa from controlpanel.bika_storagelocations import StorageLocations # noqa from controlpanel.bika_subgroups import SubGroups # noqa from controlpanel.bika_suppliers import Suppliers # noqa diff --git a/bika/lims/adapters/widgetvisibility.py b/bika/lims/adapters/widgetvisibility.py index 255fd438a9..d94f65e34a 100644 --- a/bika/lims/adapters/widgetvisibility.py +++ b/bika/lims/adapters/widgetvisibility.py @@ -132,7 +132,7 @@ class ScheduledSamplingFieldsVisibility(SenaiteATWidgetVisibility): def __init__(self, context): super(ScheduledSamplingFieldsVisibility, self).__init__( context=context, sort=10, - field_names=["ScheduledSamplingSampler", "SamplingRound"]) + field_names=["ScheduledSamplingSampler"]) def isVisible(self, field, mode="view", default="visible"): if not self.context.bika_setup.getScheduleSamplingEnabled(): diff --git a/bika/lims/browser/analysisrequest/add2.py b/bika/lims/browser/analysisrequest/add2.py index 484f67aeaa..3ee26c812c 100644 --- a/bika/lims/browser/analysisrequest/add2.py +++ b/bika/lims/browser/analysisrequest/add2.py @@ -855,9 +855,6 @@ def get_client_info(self, obj): "Specification": { "getClientUID": [uid, ""], }, - "SamplingRound": { - "getParentUID": [uid], - }, "Sample": { "getClientUID": [uid], }, diff --git a/bika/lims/browser/client/__init__.py b/bika/lims/browser/client/__init__.py index 03bc80b542..8559819301 100644 --- a/bika/lims/browser/client/__init__.py +++ b/bika/lims/browser/client/__init__.py @@ -22,11 +22,9 @@ from views.analysisrequests import ClientAnalysisRequestsView from views.analysisprofiles import ClientAnalysisProfilesView from views.artemplates import ClientARTemplatesView -from views.srtemplates import ClientSamplingRoundTemplatesView from views.samplepoints import ClientSamplePointsView from views.analysisspecs import ClientAnalysisSpecsView from views.attachments import ClientAttachmentsView from views.orders import ClientOrdersView from views.contacts import ClientContactsView from views.contacts import ClientContactVocabularyFactory -from views.samplingrounds import ClientSamplingRoundsView diff --git a/bika/lims/browser/client/configure.zcml b/bika/lims/browser/client/configure.zcml index 3edad7d5d4..88ad63ce32 100644 --- a/bika/lims/browser/client/configure.zcml +++ b/bika/lims/browser/client/configure.zcml @@ -43,14 +43,6 @@ layer="bika.lims.interfaces.IBikaLIMS" /> - - - - diff --git a/bika/lims/browser/client/views/samplingrounds.py b/bika/lims/browser/client/views/samplingrounds.py deleted file mode 100644 index e65de349bc..0000000000 --- a/bika/lims/browser/client/views/samplingrounds.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- 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 Products.CMFCore.utils import getToolByName -from bika.lims import bikaMessageFactory as _ -from bika.lims.controlpanel.bika_samplingrounds import SamplingRoundsView - - -class ClientSamplingRoundsView(SamplingRoundsView): - """This is displayed in the "Sampling Rounds" tab on each client - """ - - def __init__(self, context, request): - super(ClientSamplingRoundsView, self).__init__(context, request) - self.contentFilter = { - 'portal_type': 'SamplingRound', - 'sort_on': 'sortable_title', - 'path': { - "query": "/".join(self.context.getPhysicalPath()), - "level": 0}, - } - self.title = self.context.translate(_("Client Sampling Rounds")) - self.context_actions = { - _('Add'): {'url': '++add++SamplingRound', # To work with dexterity - 'permission': 'Add portal content', - 'icon': '++resource++bika.lims.images/add.png'}} - - def __call__(self): - mtool = getToolByName(self.context, 'portal_membership') - return super(ClientSamplingRoundsView, self).__call__() diff --git a/bika/lims/browser/client/views/srtemplates.py b/bika/lims/browser/client/views/srtemplates.py deleted file mode 100644 index 3ca1ae8a1f..0000000000 --- a/bika/lims/browser/client/views/srtemplates.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- 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.controlpanel.bika_srtemplates import SamplingRoundTemplatesView - - -class ClientSamplingRoundTemplatesView(SamplingRoundTemplatesView): - """ - Displays the client-specific Sampling Round Templates. - """ - - def __init__(self, context, request): - super(ClientSamplingRoundTemplatesView, self).__init__(context, request) diff --git a/bika/lims/browser/configure.zcml b/bika/lims/browser/configure.zcml index 9f7104f540..6b8a295504 100644 --- a/bika/lims/browser/configure.zcml +++ b/bika/lims/browser/configure.zcml @@ -48,8 +48,6 @@ - - diff --git a/bika/lims/browser/js/bika.lims.loader.js b/bika/lims/browser/js/bika.lims.loader.js index e650b05f31..28bda70961 100644 --- a/bika/lims/browser/js/bika.lims.loader.js +++ b/bika/lims/browser/js/bika.lims.loader.js @@ -72,14 +72,6 @@ window.bika.lims.controllers = { "div.overlay #client-base-edit": ['ClientOverlayHandler'], - // Client Sampling Rounds - ".template-bika-lims-content-samplingsround.portaltype-client": - ['ClientSamplingRoundAddEditView'], - - // Sampling Rounds PrintView - "#sr_publish_container": - ['SamplingRoundPrintView'], - // Reference Samples ".portaltype-referencesample.template-analyses": ['ReferenceSampleAnalysesView'], diff --git a/bika/lims/browser/js/bika.lims.samplinground.print.js b/bika/lims/browser/js/bika.lims.samplinground.print.js deleted file mode 100644 index 87b26a635a..0000000000 --- a/bika/lims/browser/js/bika.lims.samplinground.print.js +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Controller class for SamplingRound Print View - */ -function SamplingRoundPrintView() { - - var that = this; - var referrer_cookie_name = '_srpv'; - - // Allowed Paper sizes and default margins, in mm - var papersize_default = "A4"; - var default_margins = [20, 20, 20, 20]; - var papersize = { - 'A4': { - dimensions: [210, 297], - margins: [20, 20, 20, 20] }, - - 'letter': { - dimensions: [215.9, 279.4], - margins: [20, 20, 20, 20] }, - }; - - /** - * Entry-point method for AnalysisRequestPublishView - */ - that.load = function() { - - // The report will be loaded dynamically by reloadReport() - $('#report').html('').hide(); - - // Load the report - reloadReport(); - - // Store referrer in cookie in case it is lost due to a page reload - var cookiename = "sr.publish.view.referrer"; - var backurl = document.referrer; - if (backurl) { - createCookie(cookiename, backurl); - } else { - backurl = readCookie(cookiename); - // Fallback to portal_url instead of staying inside publish. - if (!backurl) { - backurl = portal_url; - } - } - - $('#sel_format').change(function(e) { - reloadReport(); - }); - - $('#sel_layout').change(function(e) { - $('body').removeClass($('body').attr('data-layout')); - $('body').attr('data-layout', $(this).val()); - $('body').addClass($(this).val()); - reloadReport(); - }); - - $('#cancel_button').click(function(e) { - location.href=backurl; - }); - $('#print_button').click(function(e) { - e.preventDefault(); - var url = window.location.href; - $('#sr_publish_container').animate({opacity:0.4}, 'slow'); - var count = $('#sr_publish_container #report .report_body').length; - $('#sr_publish_container #report .report_body').each(function(){ - var rephtml = $(this).clone().wrap('
').parent().html(); - var repstyle = $('#report-style').clone().wrap('
').parent().html(); - repstyle += $('#layout-style').clone().wrap('
').parent().html(); - var form='
' + - '' + - '' + - '' + - '
'; - $('body').html(form); - document.forms.form.submit(); - }); - }); - }; - - function get(name){ - if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search)) - return decodeURIComponent(name[1]); - } - - function load_barcodes() { - // Barcode generator - $('.barcode').each(function() { - var id = $(this).attr('data-id'); - var code = $(this).attr('data-code'); - var barHeight = $(this).attr('data-barHeight'); - var addQuietZone = $(this).attr('data-addQuietZone'); - var showHRI = $(this).attr('data-showHRI'); - $(this).barcode(id, code, - {'barHeight': parseInt(barHeight), - 'addQuietZone': Boolean(addQuietZone), - 'showHRI': Boolean(showHRI) }); - }); - } - - function convert_svgs() { - $('svg').each(function(e) { - var svg = $("
").append($(this).clone()).html(); - var img = window.bika.lims.CommonUtils.svgToImage(svg); - $(this).replaceWith(img); - }); - } - - /** - * Re-load the report view in accordance to the values set in the - * options panel (report format, pagesize, QC visible, etc.) - */ - function reloadReport() { - var url = window.location.href; - var template = $('#sel_format').val(); - if ($('#report:visible').length > 0) { - $('#report').fadeTo('fast', 0.4); - } - $.ajax({ - url: url, - type: 'POST', - async: true, - data: { "template": template} - }) - .always(function(data) { - var htmldata = data; - cssdata = $(htmldata).find('#report-style').html(); - $('#report-style').html(cssdata); - htmldata = $(htmldata).find('#report').html(); - $('#report').html(htmldata); - $('#report').fadeTo('fast', 1); - load_barcodes(); - load_layout(); - convert_svgs(); - }); - } - - /** - * Applies the selected layout (A4, US-letter) to the reports view, - * splits each report in pages depending on the layout and margins - * and applies the dynamic footer and/or header if required. - * In fact, this method makes the html ready to be printed via - * Weasyprint. - */ - function load_layout() { - // Set page layout (DIN-A4, US-letter, etc.) - var currentlayout = $('#sel_layout').val(); - // Dimensions. All expressed in mm - var dim = { - size: papersize[currentlayout].size, - outerWidth: papersize[currentlayout].dimensions[0], - outerHeight: papersize[currentlayout].dimensions[1], - marginTop: papersize[currentlayout].margins[0], - marginRight: papersize[currentlayout].margins[1], - marginBottom: papersize[currentlayout].margins[2], - marginLeft: papersize[currentlayout].margins[3], - width: papersize[currentlayout].dimensions[0]-papersize[currentlayout].margins[1]-papersize[currentlayout].margins[3], - height: papersize[currentlayout].dimensions[1]-papersize[currentlayout].margins[0]-papersize[currentlayout].margins[2] - }; - - var layout_style = - '@page { size: ' + dim.size + ' !important;' + - ' width: ' + dim.width + 'mm !important;' + - ' margin: 0mm '+dim.marginRight+'mm 0mm '+dim.marginLeft+'mm !important;'; - $('#layout-style').html(layout_style); - $('#sr_publish_container').css({'width':dim.width + 'mm', 'padding': '0mm '+dim.marginRight + 'mm 0mm '+dim.marginLeft +'mm '}); - $('#sr_publish_header').css('margin', '0mm -'+dim.marginRight + 'mm 0mm -' +dim.marginLeft+'mm'); - $('div.report_body').css({'width': dim.width + 'mm', 'max-width': dim.width + 'mm', 'min-width': dim.width + 'mm'}); - - // Iterate for each AR report and apply the dimensions, header, - // footer, etc. - $('div.report_body').each(function(i) { - - var arbody = $(this); - - // Header defined for this AR Report? - // Note that if the header of the report is taller than the - // margin, the header will be dismissed. - var header_html = ''; - var header_height = $(header_html).outerHeight(true); - if ($(this).find('.page-header').length > 0) { - var pgh = $(this).find('.page-header').first(); - header_height = parseFloat($(pgh).outerHeight(true)); - if (header_height > mmTopx(dim.marginTop)) { - // Footer too tall - header_html = ""; - header_height = parseFloat($(header_html)); - } else { - header_html = ''; - } - $(this).find('.page-header').remove(); - } - - // Footer defined for this AR Report? - // Note that if the footer of the report is taller than the - // margin, the footer will be dismissed - var footer_html = ''; - var footer_height = $(footer_html).outerHeight(true); - if ($(this).find('.page-footer').length > 0) { - var pgf = $(this).find('.page-footer').first(); - footer_height = parseFloat($(pgf).outerHeight(true)); - if (footer_height > mmTopx(dim.marginBottom)) { - // Footer too tall - footer_html = ""; - footer_height = parseFloat($(footer_html)); - } else { - footer_html = ''; - } - $(this).find('.page-footer').remove(); - } - - // Remove undesired and orphan page breaks - $(this).find('.page-break').remove(); - if ($(this).find('div').last().hasClass('manual-page-break')) { - $(this).find('div').last().remove(); - } - if ($(this).find('div').first().hasClass('manual-page-break')) { - $(this).find('div').first().remove(); - } - - // Top offset by default. The position in which the report - // starts relative to the top of the window. Used later to - // calculate when a page-break is needed. - var topOffset = $(this).position().top; - var maxHeight = mmTopx(dim.height); - var elCurrent = null; - var elOutHeight = 0; - var contentHeight = 0; - var pagenum = 1; - var pagecounts = Array(); - - // Iterate through all div children to find the suitable - // page-break points, split the report and add the header - // and footer as well as pagination count as required. - // - // IMPORTANT - // Please note that only first-level div elements from - // within div.ar_publish_body are checked and will be - // treated as nob-breakable elements. So, if a div element - // from within a div.ar_publish_body is taller than the - // maximum allowed height, that element will be omitted. - // Further improvements may solve this and handle deeply - // elements from the document, such as tables, etc. Other - // elements could be then labeled with "no-break" class to - // prevent the system to break them. - //console.log("OFF\tABS\tREL\tOUT\tHEI\tMAX"); - $(this).children('div:visible').each(function(z) { - - // Is the first page? - if (elCurrent === null) { - // Add page header if required - $(header_html).insertBefore($(this)); - topOffset = $(this).position().top; - } - - // Instead of using the height css of each element to - // know if the total height at this iteration is above - // the maximum health, we use the element's position. - // This way, we will prevent underestimations due - // non-div elements or plain text directly set inside - // the div.ar_publish_body container, not wrapped by - // other div element. - var elAbsTopPos = $(this).position().top; - var elRelTopPos = elAbsTopPos - topOffset; - var elNext = $(this).next(); - elOutHeight = parseFloat($(this).outerHeight(true)); - if ($(elNext).length > 0) { - // Calculate the height of the element according to - // the position of the next element instead of - // using the outerHeight. - elOutHeight = $(elNext).position().top-elAbsTopPos; - } - - // The current element is taller than the maximum? - if (elOutHeight > maxHeight) { - console.warn("Element with id "+$(this).attr('id')+ - " has a height above the maximum: "+ - elOutHeight); - } - - // Accumulated height - contentHeight = elRelTopPos + elOutHeight; - /*console.log(Math.floor(topOffset) + "\t" + - Math.floor(elAbsTopPos) + "\t" + - Math.floor(elRelTopPos) + "\t" + - Math.floor(elOutHeight) + "\t" + - Math.floor(contentHeight) + "\t" + - Math.floor(maxHeight) + "\t" + - '#'+$(this).attr('id')+"."+$(this).attr('class'));*/ - - if (contentHeight > maxHeight || - $(this).hasClass('manual-page-break')) { - // The content is taller than the allowed height - // or a manual page break reached. Add a page break. - var paddingTopFoot = maxHeight - elRelTopPos; - var manualbreak = $(this).hasClass('manual-page-break'); - var restartcount = manualbreak && $(this).hasClass('restart-page-count'); - var aboveBreakHtml = "
"; - var pageBreak = "
"; - $(aboveBreakHtml + footer_html + pageBreak + header_html).insertBefore($(this)); - topOffset = $(this).position().top; - if (manualbreak) { - $(this).hide(); - if (restartcount) { - // The page count needs to be restarted! - pagecounts.push(pagenum); - pagenum = 0; - } - } - contentHeight = $(this).outerHeight(true); - pagenum += 1; - } - $(this).css('width', '100%'); - elCurrent = $(this); - }); - - // Document end-footer - if (elCurrent !== null) { - var paddingTopFoot = maxHeight - contentHeight; - var aboveBreakHtml = "
"; - var pageBreak = "
"; - pagecounts.push(pagenum); - $(aboveBreakHtml + footer_html + pageBreak).insertAfter($(elCurrent)); - } - - // Wrap all elements in pages - var split_at = 'div.page-header'; - $(this).find(split_at).each(function() { - $(this).add($(this).nextUntil(split_at)).wrapAll("
"); - }); - - // Move headers and footers out of the wrapping and assign - // the top and bottom margins - $(this).find('div.page-header').each(function() { - var baseheight = $(this).height(); - $(this).css({'height': pxTomm(baseheight)+"mm", - 'margin': 0, - 'padding': (pxTomm(mmTopx(dim.marginTop) - baseheight)+"mm 0 0 0")}); - $(this).parent().before(this); - }); - $(this).find('div.page-break').each(function() { - $(this).parent().after(this); - }); - $(this).find('div.page-footer').each(function() { - $(this).css({'height': dim.marginBottom+"mm", - 'margin': 0, - 'padding': 0}); - $(this).parent().after(this); - }); - - // Page numbering - pagenum = 1; - var pagecntidx = 0; - $(this).find('.page-current-num,.page-total-count,div.page-break').each(function() { - if ($(this).hasClass('page-break')) { - if ($(this).hasClass('restart-page-count')) { - pagenum = 1; - pagecntidx += 1; - } else { - pagenum = parseInt($(this).attr('data-pagenum')) + 1; - } - } else if ($(this).hasClass('page-current-num')) { - $(this).html(pagenum); - } else { - $(this).html(pagecounts[pagecntidx]); - } - }); - }); - // Remove manual page breaks - $('.manual-page-break').remove(); - } -} -var mmTopx = function(mm) { - var px = parseFloat(mm*$('#my_mm').height()); - return px > 0 ? Math.ceil(px) : Math.floor(px); -}; -var pxTomm = function(px){ - var mm = parseFloat(px/$('#my_mm').height()); - return mm > 0 ? Math.floor(mm) : Math.ceil(mm); -}; diff --git a/bika/lims/browser/js/bika.lims.samplingrounds.js b/bika/lims/browser/js/bika.lims.samplingrounds.js deleted file mode 100644 index c9291b6f03..0000000000 --- a/bika/lims/browser/js/bika.lims.samplingrounds.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Controller class for Client's sampling round Add/Edit view - */ -function ClientSamplingRoundAddEditView() { - - var that = this; - - /** - * Entry-point method for ClientSamplingRoundAddEditView - */ - that.load = function() { - /** - * Allow to fill out the fields manually or automatically by selecting a Sampling Round Template (ReferenceField). - */ - $('select#form-widgets-sr_template') - .bind('click', function () { - if (hasSamplingRoundTemplateData()) { - setSamplingRoundTemplateData(); - } - else { - unsetSamplingRoundTemplate(); - } - }); - // Hiding not needed buttons - $("button[name='upButton']").hide(); - $("button[name='downButton']").hide(); - }; - - function hasSamplingRoundTemplateData() { - /** - * Checks if the sampling rounds template field has a sampling rounds template selected. - * :returns: True if the field has data and False if the field contains "--NOVALUE--" - */ - var value = $('select#form-widgets-sr_template').val(); - return value != "--NOVALUE--"; - } - - function setSamplingRoundTemplateData() { - /** - * This function gets the Sampling Rounds Template information and fill out each field. - * The function also binds an event to each filled field in order to unset the Sampling Round Template - * if one of the fields change. - */ - - // Getting the Sampling Round Template's data - var srt_uid = $('select#form-widgets-sr_template').val(); - if (srt_uid === undefined || srt_uid === null){ - srt_uid=''; - } - var sampler, department, samp_freq, instructions, artemplates_uids; - var request_data = { - catalog_name: "portal_catalog", - UID: srt_uid, - content_type: 'SRTemplate' - }; - window.bika.lims.jsonapi_read(request_data, function (data) { - if (data.objects && data.objects.length > 0) { - var ob = data.objects[0]; - sampler = ob['Sampler']; - department = ob['Department_uid']; - samp_freq = ob['SamplingDaysFrequency']; - instructions = ob['Instructions']; - artemplates_uids = ob['ARTemplates_uid']; - } - // Writing the Sampling Round Template's data - $("select#form-widgets-sampler").val(sampler); - $("select#form-widgets-department").val(department); - $("input#form-widgets-sampling_freq").val(samp_freq); - $("textarea#form-widgets-instructions").val(instructions); - // Setting Analysis Request Template - // Moving all options contained in "to" box to the "from" box - var to_options = $("select#form-widgets-model-to option"); - if(to_options.length >=1) { - to_options.each(function () { - $(this).click(); - }); - $("button[name='to2fromButton']").click(); // Trigger the widget JS - } - if (artemplates_uids) { - for (var i = 0; i <= artemplates_uids.length; i++) { - $("option[value='" + artemplates_uids[i] + "']").attr('selected', 'selected'); - } - } - if(artemplates_uids.length > 0) { - $("button[name='from2toButton']").click(); // Trigger the widget JS - } - }); - // Binding the unsetSamplingRoundsTemplate function - $("select#form-widgets-sampler," + - "select#form-widgets-department," + - "input#form-widgets-sampling_freq," + - "textarea#form-widgets-instructions") - .bind('change copy selected', function () { - unsetSamplingRoundTemplate(); - }); - $("button[name='to2fromButton'], button[name='from2toButton']") - .bind('blur', function () { - unsetSamplingRoundTemplate(); - }); - } - - function unsetSamplingRoundTemplate(){ - /** - This function disables the selected sampling Round Template and cut all bindings done in - setSamplingRoundTemplate(). - */ - $('select#form-widgets-sr_template').val("--NOVALUE--"); - $("select#form-widgets-sampler," + - "select#form-widgets-department," + - "input#form-widgets-sampling_freq," + - "textarea#form-widgets-instructions," + - "textarea#form-widgets-instructions," + - "button[name='to2fromButton']," + - "button[name='from2toButton']").unbind(); - } -} diff --git a/bika/lims/browser/samplinground/__init__.py b/bika/lims/browser/samplinground/__init__.py deleted file mode 100644 index b54d2c0984..0000000000 --- a/bika/lims/browser/samplinground/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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. diff --git a/bika/lims/browser/samplinground/add.py b/bika/lims/browser/samplinground/add.py deleted file mode 100644 index 2f42aa5652..0000000000 --- a/bika/lims/browser/samplinground/add.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- 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 plone.dexterity.browser import add -from AccessControl import Unauthorized - -class AddForm(add.DefaultAddForm): - """ - This class is made to check the add form permissions against client's contact users - """ - def __call__(self): - # Checking current user permissions - if self.context.hasUserAddEditPermission(): - return add.DefaultAddForm.__call__(self) - else: - raise Unauthorized diff --git a/bika/lims/browser/samplinground/analysisrequests.py b/bika/lims/browser/samplinground/analysisrequests.py deleted file mode 100644 index ffa24f1c29..0000000000 --- a/bika/lims/browser/samplinground/analysisrequests.py +++ /dev/null @@ -1,267 +0,0 @@ -# -*- 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 bikaMessageFactory as _ -from bika.lims.browser.analysisrequest import AnalysisRequestAddView as _ARAV -from bika.lims.browser.analysisrequest import AnalysisRequestsView as _ARV -from bika.lims.permissions import * -from plone.app.layout.globals.interfaces import IViewView -from Products.CMFCore.utils import getToolByName -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from zope.interface import implements -from bika.lims.utils import t -from copy import copy - - -class AnalysisRequestsView(_ARV, _ARAV): - implements(IViewView) - - def __init__(self, context, request): - super(AnalysisRequestsView, self).__init__(context, request) - self.catalog = "portal_catalog" - SamplingWorkflowEnabled = self.context.bika_setup.getSamplingWorkflowEnabled() - self.columns = { - 'securitySealIntact': {'title': _('Security Seal Intact'), - 'toggle': True}, - 'samplingRoundTemplate': {'title': _('Sampling Round Template'), - 'toggle': True}, - 'getId': {'title': _('Request ID'), - 'index': 'getId'}, - 'getDateSampled': {'title': _('Date Sampled'), - 'index': 'getDateSampled', - 'toggle': True, - 'input_class': 'datetimepicker', - 'input_width': '10'}, - 'state_title': {'title': _('State'), - 'index': 'review_state'}, - 'getProfilesTitle': {'title': _('Profile'), - 'toggle': False}, - 'getTemplateTitle': {'title': _('Template'), - 'toggle': False}, - } - - self.review_states = [ - {'id': 'default', - 'title': _('Active'), - 'contentFilter': {'is_active': True, - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'sample'}, - {'id': 'preserve'}, - {'id': 'receive'}, - {'id': 'retract'}, - {'id': 'verify'}, - {'id': 'prepublish'}, - {'id': 'publish'}, - {'id': 'republish'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'sample_due', - 'title': _('Due'), - 'contentFilter': {'review_state': ('to_be_sampled', - 'to_be_preserved', - 'sample_due'), - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'sample'}, - {'id': 'preserve'}, - {'id': 'receive'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'sample_received', - 'title': _('Received'), - 'contentFilter': {'review_state': 'sample_received', - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'prepublish'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'to_be_verified', - 'title': _('To be verified'), - 'contentFilter': {'review_state': 'to_be_verified', - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'retract'}, - {'id': 'verify'}, - {'id': 'prepublish'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'verified', - 'title': _('Verified'), - 'contentFilter': {'review_state': 'verified', - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'publish'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'published', - 'title': _('Published'), - 'contentFilter': {'review_state': ('published', 'invalid'), - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'republish'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'cancelled', - 'title': _('Cancelled'), - 'contentFilter': {'is_active': False, - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'invalid', - 'title': _('Invalid'), - 'contentFilter': {'review_state': 'invalid', - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'assigned', - 'title': "" % ( - t(_("Assigned")), self.portal_url), - 'contentFilter': {'assigned_state': 'assigned', - 'review_state': ('sample_received', 'to_be_verified', - 'attachment_due', 'verified', - 'published'), - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'retract'}, - {'id': 'verify'}, - {'id': 'prepublish'}, - {'id': 'publish'}, - {'id': 'republish'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - {'id': 'unassigned', - 'title': "" % ( - t(_("Unassigned")), self.portal_url), - 'contentFilter': {'assigned_state': 'unassigned', - 'review_state': ('sample_received', 'to_be_verified', - 'attachment_due', 'verified', - 'published'), - 'sort_on': 'created', - 'sort_order': 'reverse'}, - 'transitions': [{'id': 'receive'}, - {'id': 'retract'}, - {'id': 'verify'}, - {'id': 'prepublish'}, - {'id': 'publish'}, - {'id': 'republish'}, - {'id': 'cancel'}, - {'id': 'reinstate'}], - 'custom_transitions': [], - 'columns': ['securitySealIntact', - 'getId', - 'samplingRoundTemplate', - 'getDateSampled', - 'state_title']}, - ] - - def contentsMethod(self, contentFilter): - pc = getToolByName(self.context, 'portal_catalog') - if 'SamplingRoundUID' not in contentFilter.keys(): - contentFilter['SamplingRoundUID'] = self.context.UID() - return pc(contentFilter) - - def __call__(self): - self.context_actions = {} - mtool = getToolByName(self.context, 'portal_membership') - if mtool.checkPermission(AddAnalysisRequest, self.portal): - # We give the number of analysis request templates in order to fill out the analysis request form - # automatically :) - num_art = len(self.context.ar_templates) - self.context_actions[self.context.translate(_('Add new'))] = { - 'url': self.context.aq_parent.absolute_url() + \ - "/portal_factory/" - "AnalysisRequest/Request new analyses/ar_add?samplinground=" - + self.context.UID() + "&ar_count=" + str(num_art), - 'icon': '++resource++bika.lims.images/add.png'} - return super(AnalysisRequestsView, self).__call__() - - def folderitem(self, obj, item, index): - # Call the folderitem method from the base class - item = _ARV.folderitem(self, obj, item, index) - # In sampling rounds, analysis request list will be listed per Sample - # Partition/Container Obtaining analysis requests - # TODO-performance: don't get the full object - obj = obj.getObject() - # Getting the sampling round template uid - srTemplateUID = obj.getSamplingRound().sr_template if obj.getSamplingRound().sr_template else '' - # Getting the sampling round object - catalog = getToolByName(self.context, 'uid_catalog') - srTemplateObj = catalog(UID=srTemplateUID)[0].getObject() if catalog(UID=srTemplateUID) else None - item['samplingRoundTemplate'] = '' - if srTemplateObj: - item['samplingRoundTemplate'] = srTemplateObj.title - item['replace']['samplingRoundTemplate'] = \ - "%s" % ( - srTemplateObj.absolute_url, item['samplingRoundTemplate']) - item['securitySealIntact'] = '' - return item diff --git a/bika/lims/browser/samplinground/configure.zcml b/bika/lims/browser/samplinground/configure.zcml deleted file mode 100644 index bb2ca73b03..0000000000 --- a/bika/lims/browser/samplinground/configure.zcml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/bika/lims/browser/samplinground/edit.py b/bika/lims/browser/samplinground/edit.py deleted file mode 100644 index e2b1ff3d60..0000000000 --- a/bika/lims/browser/samplinground/edit.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- 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 plone.dexterity.browser import edit -from AccessControl import Unauthorized - -class EditForm(edit.DefaultEditForm): - """ - This class is made to check the edit form permissions against client's contact users - """ - def __call__(self): - # Checking current user permissions - if self.context.hasUserAddEditPermission(): - return edit.DefaultEditForm.__call__(self) - else: - raise Unauthorized diff --git a/bika/lims/browser/samplinground/printform.py b/bika/lims/browser/samplinground/printform.py deleted file mode 100644 index bf68dfde21..0000000000 --- a/bika/lims/browser/samplinground/printform.py +++ /dev/null @@ -1,265 +0,0 @@ -# -*- 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 bikaMessageFactory as _, t -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import safe_unicode -from bika.lims.utils import to_utf8, createPdf -from bika.lims.browser import BrowserView -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from plone.resource.utils import iterDirectoriesOfType, queryResourceDirectory -import App -import tempfile -import os -import glob -import traceback - - -class PrintForm(BrowserView): - template = ViewPageTemplateFile("templates/print_form.pt") - _DEFAULT_TEMPLATE = 'default_form.pt' - _TEMPLATES_DIR = 'templates/print' - _TEMPLATES_ADDON_DIR = 'samplingrounds' - _current_sr_index = 0 - _samplingrounds = [] - - def __call__(self): - if self.context.portal_type == 'SamplingRound': - self._samplingrounds = [self.context] - - elif self.context.portal_type == 'SamplingRounds' \ - and self.request.get('items', ''): - uids = self.request.get('items').split(',') - uc = getToolByName(self.context, 'uid_catalog') - self._samplingrounds = [obj.getObject() for obj in uc(UID=uids)] - - else: - # Warn and redirect to referer - logger.warning('PrintView: type not allowed: %s' % - self.context.portal_type) - self.destination_url = self.request.get_header("referer", - self.context.absolute_url()) - - # Do print? - if self.request.form.get('pdf', '0') == '1': - response = self.request.response - response.setHeader("Content-type", "application/pdf") - response.setHeader("Content-Disposition", "inline") - response.setHeader("filename", "temp.pdf") - return self.pdfFromPOST() - else: - return self.template() - - def getSamplingRoundObj(self): - """Returns the sampling round object - """ - return self.context - - def getSRTemplates(self): - """ - Returns a DisplayList with the available templates found in - browser/samplinground/templates/ - """ - this_dir = os.path.dirname(os.path.abspath(__file__)) - templates_dir = os.path.join(this_dir, self._TEMPLATES_DIR) - tempath = '%s/%s' % (templates_dir, '*.pt') - templates = [t.split('/')[-1] for t in glob.glob(tempath)] - out = [] - for template in templates: - out.append({'id': template, 'title': template[:-3]}) - for templates_resource in iterDirectoriesOfType(self._TEMPLATES_ADDON_DIR): - prefix = templates_resource.__name__ - templates = [ - tpl for tpl in templates_resource.listDirectory() - if tpl.endswith('.pt') - ] - for template in templates: - out.append({ - 'id': '{0}:{1}'.format(prefix, template), - 'title': '{0} ({1})'.format(template[:-3], prefix), - }) - return out - - def getFormTemplate(self): - """Returns the current samplinground rendered with the template - specified in the request (param 'template'). - Moves the iterator to the next samplinground available. - """ - templates_dir = self._TEMPLATES_DIR - embedt = self.request.get('template', self._DEFAULT_TEMPLATE) - if embedt.find(':') >= 0: - prefix, embedt = embedt.split(':') - templates_dir = queryResourceDirectory(self._TEMPLATES_ADDON_DIR, prefix).directory - embed = ViewPageTemplateFile(os.path.join(templates_dir, embedt)) - reptemplate = "" - try: - reptemplate = embed(self) - except: - tbex = traceback.format_exc() - wsid = self._samplingrounds[self._current_sr_index].id - reptemplate = "
%s - %s '%s':
%s
" % (wsid, _("Unable to load the template"), embedt, tbex) - if self._current_sr_index < len(self._samplingrounds): - self._current_sr_index += 1 - return reptemplate - - def getCSS(self): - """ Returns the css style to be used for the current template. - If the selected template is 'default.pt', this method will - return the content from 'default.css'. If no css file found - for the current template, returns empty string - """ - template = self.request.get('template', self._DEFAULT_TEMPLATE) - content = '' - if template.find(':') >= 0: - prefix, template = template.split(':') - resource = queryResourceDirectory( - self._TEMPLATES_ADDON_DIR, prefix) - css = '{0}.css'.format(template[:-3]) - if css in resource.listDirectory(): - content = resource.readFile(css) - else: - this_dir = os.path.dirname(os.path.abspath(__file__)) - templates_dir = os.path.join(this_dir, self._TEMPLATES_DIR) - path = '%s/%s.css' % (templates_dir, template[:-3]) - with open(path, 'r') as content_file: - content = content_file.read() - return content - - def getAnalysisRequestTemplatesInfo(self): - """ - Returns a lost of dicts with the analysis request templates infomration - [{'uid':'xxxx','id':'xxxx','title':'xxx','url':'xxx'}, ...] - """ - arts_list = [] - for art in self.context.ar_templates: - pc = getToolByName(self.context, 'portal_catalog') - contentFilter = {'portal_type': 'ARTemplate', - 'UID': art} - art_brain = pc(contentFilter) - if len(art_brain) == 1: - art_obj = art_brain[0].getObject() - arts_list.append({ - 'uid': art_obj.UID(), - 'id': art_obj.id, - 'title': art_obj.title, - 'url': art_obj.absolute_url(), - }) - return arts_list - - def getAnalysisRequestBySample(self): - """ - Returns a list of dictionaries sorted by Sample Partition/Container - [{'requests and partition info'}, ...] - """ - # rows will contain the data for each html row - rows = [] - # columns will be used to sort and define the columns - columns = { - 'column_order': [ - 'sample_id', - 'sample_type', - 'sampling_point', - 'sampling_date', - 'analyses', - ], - 'titles': { - 'sample_id': _('Sample ID'), - 'sample_type': _('Sample Type'), - 'sampling_point': _('Sampling Point'), - 'sampling_date': _('Sampling Date'), - 'analyses': _('Analysis'), - } - } - ars = self.context.getAnalysisRequests() - for ar in ars: - ar = ar.getObject() - arcell = False - analyses = ar.getAnalyses() - numans = len(analyses) - for analysis in analyses: - row = { - 'sample_id': { - 'hidden': True if arcell else False, - 'rowspan': numans, - 'value': ar.getId(), - }, - 'sample_type': { - 'hidden': True if arcell else False, - 'rowspan': numans, - 'value': ar.getSampleType().title, - }, - 'sampling_point': { - 'hidden': True if arcell else False, - 'rowspan': numans, - 'value': ar.getSamplePoint().title if ar.getSamplePoint() else '', - }, - 'sampling_date': { - 'hidden': True if arcell else False, - 'rowspan': numans, - 'value': self.context.sampling_date, - }, - 'analyses': { - 'title': analysis.title, - 'units': analysis.getUnit, - }, - } - rows.append(row) - arcell = True - - # table will contain the data that from where the html - # will take the info - table = { - 'columns': columns, - 'rows': rows, - } - return table - - def getLab(self): - return self.context.bika_setup.laboratory.getLabURL() - - def getLogo(self): - portal = self.context.portal_url.getPortalObject() - return "%s/logo_print.png" % portal.absolute_url() - - def pdfFromPOST(self): - """ - It returns the pdf for the sampling rounds printed - """ - html = self.request.form.get('html') - style = self.request.form.get('style') - reporthtml = "%s
%s" % (style, html) - return self.printFromHTML(safe_unicode(reporthtml).encode('utf-8')) - - def printFromHTML(self, sr_html): - """ - Tis function generates a pdf file from the html - :sr_html: the html to use to generate the pdf - """ - # HTML written to debug file - debug_mode = App.config.getConfiguration().debug_mode - if debug_mode: - tmp_fn = tempfile.mktemp(suffix=".html") - open(tmp_fn, "wb").write(sr_html) - - # Creates the pdf - # we must supply the file ourself so that createPdf leaves it alone. - pdf_fn = tempfile.mktemp(suffix=".pdf") - pdf_report = createPdf(htmlreport=sr_html, outfile=pdf_fn) - return pdf_report diff --git a/bika/lims/browser/samplinground/templates/print/default_form.css b/bika/lims/browser/samplinground/templates/print/default_form.css deleted file mode 100644 index 96c36c588c..0000000000 --- a/bika/lims/browser/samplinground/templates/print/default_form.css +++ /dev/null @@ -1,60 +0,0 @@ - -.barcode-container { - width:100%; -} -div.report_body { - font-size:0.8em; -} -div.report_body a { - color:#000; - text-decoration:none; -} -div.report_body h1 { - padding-top:15px; - font-size:1.7em; -} -div#title{ - font-size:1.5em; -} -.label { - font-weight: bold; -} -.table-text { - position: relative; -} -.data-input{ - border: 1px solid #cdcdcd; - height: 20px; -} -table.samples-grid tr td { - vertical-align:top; - border: 1px solid #cdcdcd; - padding: 5px; -} -table thead th{ - border: 1px solid #cdcdcd; - background-color: #808080; -} -#sampling-round-info div { - float: left; - font-size: 0.85em; - position: relative; - width: 150px; -} -span.units { - font-size: 0.7em; -} -div.lab-logo { - float: right; - position: relative; -} -p { - margin:0; - padding:0; -} -.clearfix { - clear:both; -} -#sampling-round-data-entry { - clear: both; -} diff --git a/bika/lims/browser/samplinground/templates/print/default_form.pt b/bika/lims/browser/samplinground/templates/print/default_form.pt deleted file mode 100644 index ba7c04f27d..0000000000 --- a/bika/lims/browser/samplinground/templates/print/default_form.pt +++ /dev/null @@ -1,134 +0,0 @@ - - - - - -
-
-

Description

-

-
-
-

Sampling round template

-

- - - -

-
-

Sampler

-

-
-
-

Department

-

- - - -

-
-
-

Sampling frequency

-

-
-
-

Sampling date

-

-
-
-

Environmental conditions

-

-
-
-

Sampling instructions

-

-
-
-

Samples templates

-

- - -
-
-

-
-
-

Sample points

-

-
-
-

Containers

-

-
-
 
-
- -
- - - - - - - - - - - - - - - - - -
-
-
-
- -
- -
-
-
-
-
-
- -
-
diff --git a/bika/lims/browser/samplinground/templates/print_form.pt b/bika/lims/browser/samplinground/templates/print_form.pt deleted file mode 100644 index 0d9ff019f3..0000000000 --- a/bika/lims/browser/samplinground/templates/print_form.pt +++ /dev/null @@ -1,209 +0,0 @@ - - - - - -
- - -
-
-
-
- - -
-
- - -
-
-
-    - -
-
- - - -
-
-
- - -
- - diff --git a/bika/lims/browser/srtemplate/__init__.py b/bika/lims/browser/srtemplate/__init__.py deleted file mode 100644 index b54d2c0984..0000000000 --- a/bika/lims/browser/srtemplate/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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. diff --git a/bika/lims/browser/srtemplate/artemplates.py b/bika/lims/browser/srtemplate/artemplates.py deleted file mode 100644 index 08267a7a5e..0000000000 --- a/bika/lims/browser/srtemplate/artemplates.py +++ /dev/null @@ -1,211 +0,0 @@ -# -*- 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 AccessControl import ClassSecurityInfo -from bika.lims import bikaMessageFactory as _ -from bika.lims.utils import t -from bika.lims.browser.bika_listing import BikaListingView -from Products.Archetypes.Registry import registerWidget -from Products.Archetypes.Widget import TypesWidget -from Products.CMFCore.utils import getToolByName - - -class AnalysisRequestTemplatesView(BikaListingView): - """ BIKA listing to display ARTemplates for an SRTemplate. - """ - - def __init__(self, context, request): - super(AnalysisRequestTemplatesView, self).__init__(context, request) - self.catalog = "bika_setup_catalog" - self.context_actions = {} - self.base_url = self.context.absolute_url() - self.view_url = self.base_url - - self.show_select_row = False - self.show_select_all_checkbox = False - self.show_column_toggles = False - self.show_select_column = True - self.show_categories = True - self.expand_all_categories = True - self.pagesize = 50 - self.title = self.context.translate(_("Sample Templates")) - self.icon = self.portal_url + "/++resource++bika.lims.images/artemplate_big.png" - self.form_id = "artemplates" - self.columns = { - 'title': { - 'title': _('Template Title'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'SamplePoint': { - 'title': _('Sample Point'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'SampleType': { - 'title': _('Sample Type'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'Composite': { - 'title': _('Composite Y/N'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'ContainerTitle': { - 'title': _('Container Title'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'ContainerVolume': { - 'title': _('Container Volume'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'Preservation': { - 'title': _('Preservation'), - 'index': 'sortable_title', - 'sortable': True, - }, - 'Sampler': { - 'title': _('Sampler'), - 'sortable': True, - }, - 'PreparationMethod': { - 'title': _('Preservation Method'), - 'index': 'sortable_title', - 'sortable': True, - }, - } - self.review_states = [ - {'id': 'default', - 'title': _('Active'), - 'contentFilter': {'is_active': True}, - 'transitions': [{'id': 'deactivate'}, ], - 'columns': ['title', - 'SamplePoint', - 'SampleType', - 'Composite', - 'ContainerTitle', - 'ContainerVolume', - 'Preservation', - #'Sampler', - 'PreparationMethod']}, - {'id': 'inactive', - 'title': _('Inactive'), - 'contentFilter': {'is_active': False}, - 'transitions': [{'id': 'activate'}, ], - 'columns': ['title', - 'SamplePoint', - 'SampleType', - 'Composite', - 'ContainerTitle', - 'ContainerVolume', - 'Preservation', - #'Sampler', - 'PreparationMethod']}, - {'id': 'all', - 'title': _('All'), - 'contentFilter': {}, - 'columns': ['title', - 'SamplePoint', - 'SampleType', - 'Composite', - 'ContainerTitle', - 'ContainerVolume', - 'Preservation', - #'Sampler', - 'PreparationMethod']}, - ] - - def contentsMethod(self, contentFilter): - return self.context.getARTemplates() - - def _buildFromPerPartition(self, item, partition): - """ - This function will get the partition info and then it'll write the container and preservation data - to the dictionary 'item' - :param item: a dict which contains the ARTeplate data columns - :param partition: a dict with some partition info - :returns: the item dict with the partition's data - """ - uc = getToolByName(self, 'uid_catalog') - container = uc(UID=partition.get('container_uid', '')) - preservation = uc(UID=partition.get('preservation_uid', '')) - if container: - container = container[0].getObject() - item['ContainerTitle'] = container.title - item['replace']['ContainerTitle'] = "%s" % \ - (container.absolute_url(), item['ContainerTitle']) - item['ContainerVolume'] = container.getCapacity() - else: - item['ContainerTitle'] = '' - item['ContainerVolume'] = '' - if preservation: - preservation = preservation[0].getObject() - item['Preservation'] = preservation.title - item['replace']['Preservation'] = "%s" % \ - (preservation.absolute_url(), item['Preservation']) - else: - item['Preservation'] = '' - item['PreparationMethod'] = '' - return item - - def folderitems(self): - items = BikaListingView.folderitems(self) - new_items = [] - for item in items: - if not item.has_key('obj'): continue - obj = item['obj'] - # Updating some ARTemplate columns - title_link = "%s" % (item['url'], item['title']) - item['replace']['title'] = title_link - if obj.getSamplePoint(): - item['SamplePoint'] = obj.getSamplePoint().title - item['replace']['SamplePoint'] = "%s" % \ - (obj.getSamplePoint().absolute_url(), item['SamplePoint']) - else: - item['SamplePoint'] = '' - if obj.getSamplePoint(): - item['SamplePoint'] = obj.getSamplePoint().title - item['replace']['SamplePoint'] = "%s" % \ - (obj.getSamplePoint().absolute_url(), item['SamplePoint']) - else: - item['SamplePoint'] = '' - if obj.getSampleType(): - item['SampleType'] = obj.getSampleType().title - item['replace']['SampleType'] = "%s" % \ - (obj.getSampleType().absolute_url(), item['SampleType']) - else: - item['SampleType'] = '' - item['Composite'] = obj.getComposite() - img_url = '' - item['replace']['Composite'] = img_url if obj.getComposite() else ' ' - - partitions = obj.getPartitions() - for partition in partitions: - c_item = item.copy() - # We ave to make a copy of 'replace' because it's a reference to a dict object - c_item['replace'] = item['replace'].copy() - # Adding the partition info - c_item = self._buildFromPerPartition(c_item, partition) - # Adding the ARTemplate item to the future list to display - new_items.append(c_item) - return new_items diff --git a/bika/lims/browser/srtemplate/configure.zcml b/bika/lims/browser/srtemplate/configure.zcml deleted file mode 100644 index 77e32d9cef..0000000000 --- a/bika/lims/browser/srtemplate/configure.zcml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/bika/lims/browser/widgets/__init__.py b/bika/lims/browser/widgets/__init__.py index bb1b901bce..af0f50bc99 100644 --- a/bika/lims/browser/widgets/__init__.py +++ b/bika/lims/browser/widgets/__init__.py @@ -33,7 +33,6 @@ from .analysisprofileanalyseswidget import AnalysisProfileAnalysesWidget from .artemplateanalyseswidget import ARTemplateAnalysesWidget from .artemplatepartitionswidget import ARTemplatePartitionsWidget -from .srtemplateartemplateswidget import SRTemplateARTemplatesWidget from .addresswidget import AddressWidget from .scheduleinputwidget import ScheduleInputWidget from .integer import IntegerWidget diff --git a/bika/lims/browser/widgets/srtemplateartemplateswidget.py b/bika/lims/browser/widgets/srtemplateartemplateswidget.py deleted file mode 100644 index ad8ae4ef8a..0000000000 --- a/bika/lims/browser/widgets/srtemplateartemplateswidget.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- 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 AccessControl import ClassSecurityInfo -from bika.lims import bikaMessageFactory as _ -from bika.lims.utils import t -from bika.lims.browser.bika_listing import BikaListingView -from Products.Archetypes.Registry import registerWidget -from Products.Archetypes.Widget import TypesWidget -from Products.CMFCore.utils import getToolByName - - -class SRTemplateARTemplatesView(BikaListingView): - """ BIKA listing to display ARTemplates for an SRTemplate. - """ - - def __init__(self, context, request, fieldvalue, allow_edit): - super(SRTemplateARTemplatesView, self).__init__(context, request) - self.catalog = "bika_setup_catalog" - self.contentFilter = { - 'portal_type': 'ARTemplate', - 'sort_on': 'title', - 'is_active': True, - } - self.context_actions = {} - self.base_url = self.context.absolute_url() - self.view_url = self.base_url - - self.show_select_row = False - self.show_select_all_checkbox = False - self.show_column_toggles = False - self.show_select_column = True - self.show_categories = True - self.expand_all_categories = True - self.pagesize = 999999 - self.allow_edit = allow_edit - self.clientUID = None - self.form_id = "artemplates" - self.columns = { - 'Title': { - 'title': _('Service'), - 'index': 'title', - 'replace_url': 'absolute_url', - 'sortable': False, - }, - } - self.review_states = [{ - 'id':'default', - 'title': _('All'), - 'contentFilter':{}, - 'columns': ['Title'], - 'transitions': [{'id':'empty'}], - }] - self.fieldvalue = fieldvalue - self.selected = [o.UID() for o in fieldvalue] - - def isItemAllowed(self, obj): - if self.clientUID is None: - self.clientUID = self.context.aq_parent.aq_parent.UID() - # Only display client's and lab's arts - if obj.aq_parent.aq_inner.meta_type == 'Client': - obj_client = obj.getClientUID() - if obj_client != self.clientUID: - return False - return True - - def folderitem(self, obj, item, index): - item['selected'] = item['uid'] in self.selected - return item - - -class SRTemplateARTemplatesWidget(TypesWidget): - _properties = TypesWidget._properties.copy() - _properties.update({ - 'macro': "bika_widgets/srtemplateartemplateswidget", - }) - - security = ClassSecurityInfo() - - security.declarePublic('process_form') - def process_form(self, instance, field, form, empty_marker = None, - emptyReturnsMarker = False): - bsc = getToolByName(instance, 'bika_setup_catalog') - value = [] - service_uids = form.get('uids', None) - return service_uids, {} - - security.declarePublic('ARTemplates') - def ARTemplates(self, field, allow_edit = False): - fieldvalue = getattr(field, field.accessor)() - view = SRTemplateARTemplatesView( - self, - self.REQUEST, - fieldvalue = fieldvalue, - allow_edit = allow_edit - ) - return view.contents_table(table_only = True) - - -registerWidget( - SRTemplateARTemplatesWidget, - title = 'SR Template Sample Templates Selector', - description = ('SR Template Sample Templates Selector'), -) diff --git a/bika/lims/content/analysisrequest.py b/bika/lims/content/analysisrequest.py index debea78513..a6e6c5abd4 100644 --- a/bika/lims/content/analysisrequest.py +++ b/bika/lims/content/analysisrequest.py @@ -116,7 +116,6 @@ from bika.lims.permissions import FieldEditSampler from bika.lims.permissions import FieldEditSamplingDate from bika.lims.permissions import FieldEditSamplingDeviation -from bika.lims.permissions import FieldEditSamplingRound from bika.lims.permissions import FieldEditScheduledSampler from bika.lims.permissions import FieldEditSpecification from bika.lims.permissions import FieldEditStorageLocation @@ -339,27 +338,6 @@ ), ), - ReferenceField( - 'SamplingRound', - allowed_types=('SamplingRound',), - relationship='AnalysisRequestSamplingRound', - mode="rw", - read_permission=View, - write_permission=FieldEditSamplingRound, - widget=ReferenceWidget( - label=_("Sampling Round"), - description=_("The assigned sampling round of this request"), - size=20, - render_own_label=True, - visible={ - 'add': 'invisible', - }, - catalog_name='portal_catalog', - base_query={}, - showOn=True, - ), - ), - ReferenceField( 'SubGroup', required=False, @@ -1167,12 +1145,6 @@ expression="here._getCreatorEmail()", widget=ComputedWidget(visible=False), ), - ComputedField( - 'SamplingRoundUID', - expression="here.getSamplingRound().UID() " \ - "if here.getSamplingRound() else ''", - widget=ComputedWidget(visible=False), - ), ComputedField( 'SamplerFullName', expression="here._getSamplerFullName()", @@ -1919,16 +1891,6 @@ def isInvalid(self): workflow = getToolByName(self, 'portal_workflow') return workflow.getInfoFor(self, 'review_state') == 'invalid' - def getSamplingRoundUID(self): - """Obtains the sampling round UID - :returns: UID - """ - sr = self.getSamplingRound() - if sr: - return sr.UID() - else: - return '' - def getStorageLocationTitle(self): """ A method for AR listing catalog metadata :return: Title of Storage Location diff --git a/bika/lims/content/configure.zcml b/bika/lims/content/configure.zcml index 3df4d05458..de43135625 100644 --- a/bika/lims/content/configure.zcml +++ b/bika/lims/content/configure.zcml @@ -7,8 +7,6 @@ i18n_domain="senaite.core"> - - [(ART.title),(ART.UID),...] - """ - l = [] - art_uids = self.ar_templates - # I have to get the catalog in this way because I can't do it with 'self'... - pc = getToolByName(api.portal.get(), 'uid_catalog') - for art_uid in art_uids: - art_obj = pc(UID=art_uid) - if len(art_obj) != 0: - l.append((art_obj[0].Title, art_uid)) - return l - - def getDepartmentInfo(self): - """ - Returns a dict with the department infomration - {'uid':'xxxx','id':'xxxx','title':'xxx','url':'xxx'} - """ - pc = getToolByName(api.portal.get(), 'portal_catalog') - contentFilter = {'portal_type': 'Department', - 'UID': self.department} - departmentlist = pc(contentFilter) - departmentdict = {'uid': '', 'id': '', 'title': '', 'url': ''} - if len(departmentlist) == 1: - department = departmentlist[0].getObject() - departmentdict = { - 'uid': department.id, - 'id': department.UID(), - 'title': department.title, - 'url': department.absolute_url(), - } - else: - from bika.lims import logger - error = "Error when looking for department with uid '%s'. " - logger.exception(error, self.department) - return departmentdict - - def getSRTemplateInfo(self): - """ - Returns a dict with the SRTemplate infomration - {'uid':'xxxx','id':'xxxx','title':'xxx','url':'xxx'} - """ - pc = getToolByName(api.portal.get(), 'portal_catalog') - contentFilter = {'portal_type': 'SRTemplate', - 'UID': self.sr_template} - srt = pc(contentFilter) - srtdict = {'uid': '', 'id': '', 'title': '', 'url': ''} - if len(srt) == 1: - template = srt[0].getObject() - srtdict = { - 'uid': template.id, - 'id': template.UID(), - 'title': template.title, - 'url': template.absolute_url(), - } - else: - from bika.lims import logger - error = "Error when looking for sr template with uid '%s'. " - logger.exception(error, self.sr_template) - return srtdict - - def getClientContact(self): - """ - Returns info from the Client contact who coordinates with the lab - """ - pc = getToolByName(api.portal.get(), 'portal_catalog') - contentFilter = {'portal_type': 'Contact', - 'id': self.client_contact} - cnt = pc(contentFilter) - cntdict = {'uid': '', 'id': '', 'fullname': '', 'url': ''} - if len(cnt) == 1: - cnt = cnt[0].getObject() - cntdict = { - 'uid': cnt.id, - 'id': cnt.UID(), - 'fullname': cnt.getFullname(), - 'url': cnt.absolute_url(), - } - else: - from bika.lims import logger - error = "Error when looking for contact with id '%s'. " - logger.exception(error, self.client_contact) - return cntdict - - def getClientInChargeAtSamplingTime(self): - """ - Returns info from the Client contact who is in charge at sampling time - """ - pc = getToolByName(api.portal.get(), 'portal_catalog') - contentFilter = {'portal_type': 'Contact', - 'id': self.client_contact_in_charge_at_sampling_time} - cnt = pc(contentFilter) - cntdict = {'uid': '', 'id': '', 'fullname': '', 'url': ''} - if len(cnt) == 1: - cnt = cnt[0].getObject() - cntdict = { - 'uid': cnt.id, - 'id': cnt.UID(), - 'fullname': cnt.getFullname(), - 'url': cnt.absolute_url(), - } - else: - from bika.lims import logger - error = "Error when looking for contact with id '%s'. " - logger.exception( - error, self.client_contact_in_charge_at_sampling_time) - return cntdict - - def hasUserAddEditPermission(self): - """ - Checks if the current user has privileges to access to the editing view. - From Jira LIMS-1549: - - Creation/Edit: Lab manager, Client Contact, Lab Clerk, Client Contact (for Client-specific SRTs) - :returns: True/False - """ - mtool = getToolByName(self, 'portal_membership') - checkPermission = mtool.checkPermission - # In bika_samplinground_workflow.csv there are defined the ModifyPortalContent statements. There is said that - # client has ModifyPortalContent permission enabled, so here we have to check if the client satisfy the - # condition wrote in the function's description - if (checkPermission(ModifyPortalContent, self) or checkPermission(AddPortalContent, self)) \ - and 'Client' in api.user.get_current().getRoles(): - # Checking if the current user is a current client's contact - userID = api.user.get_current().id - contact_objs = self.getContacts() - contact_ids = [obj.getUsername() for obj in contact_objs] - if userID in contact_ids: - return True - else: - return False - return checkPermission(ModifyPortalContent, self) or checkPermission(AddPortalContent, self) - - def workflow_script_cancel(self): - """ - When the round is cancelled, all its associated Samples and ARs are cancelled by the system. - """ - if skip(self, "cancel"): - return - self.reindexObject(idxs=["is_active", ]) - # deactivate all analysis requests in this sampling round. - analysis_requests = self.getAnalysisRequests() - for ar in analysis_requests: - ar_obj = ar.getObject() - workflow = getToolByName(self, 'portal_workflow') - if workflow.getInfoFor(ar_obj, 'review_state') != 'cancelled': - doActionFor(ar.getObject(), 'cancel') diff --git a/bika/lims/content/srtemplate.py b/bika/lims/content/srtemplate.py deleted file mode 100644 index af2a8dc6f3..0000000000 --- a/bika/lims/content/srtemplate.py +++ /dev/null @@ -1,140 +0,0 @@ -# -*- 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. - -import sys - -from AccessControl import ClassSecurityInfo -from Products.Archetypes.public import * -from Products.Archetypes.references import HoldingReference -from Products.CMFCore.utils import getToolByName -from bika.lims import bikaMessageFactory as _ -from bika.lims.browser.widgets import SRTemplateARTemplatesWidget -from bika.lims.config import PROJECTNAME -from bika.lims.content.bikaschema import BikaSchema -from bika.lims.idserver import renameAfterCreation -from bika.lims.interfaces import ISamplingRoundTemplate, IDeactivable -from bika.lims.utils import getUsers -from zope.interface import implements - -schema = BikaSchema.copy() + Schema(( - - # The default sampler for the rounds - StringField('Sampler', - required=1, - searchable=True, - vocabulary='_getSamplersDisplayList', - widget=SelectionWidget( - format='select', - label = _("Sampler"), - ), - ), - - # The department responsible for the sampling round - ReferenceField('Department', - required=1, - vocabulary_display_path_bound=sys.maxint, - allowed_types=('Department',), - vocabulary='_getDepartmentsDisplayList', - relationship='SRTemplateDepartment', - referenceClass=HoldingReference, - widget=ReferenceWidget( - checkbox_bound=0, - label = _("Department"), - description = _("The laboratory department"), - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - ), - ), - - # The number of days between recurring field trips - IntegerField('SamplingDaysFrequency', - required=1, - default=7, - widget=IntegerWidget( - label = _("Sampling Frequency"), - description=_( - "The number of days between recurring field trips"), - ), - ), - - TextField('Instructions', - searchable = True, - default_content_type = 'text/plain', - allowed_content_types= ('text/plain'), - default_output_type="text/plain", - widget = TextAreaWidget( - label=_("Instructions"), - append_only = False, - ), - ), - - ReferenceField('ARTemplates', - schemata = 'AR Templates', - required = 1, - multiValued = 1, - allowed_types = ('ARTemplate',), - relationship = 'SRTemplateARTemplate', - widget = SRTemplateARTemplatesWidget( - label=_("Sample Templates"), - description=_("Select Sample Templates to include"), - ) - ), -)) - -schema['description'].widget.visible = True -schema['title'].widget.visible = True -schema['title'].validators = ('uniquefieldvalidator',) -# Update the validation layer after change the validator in runtime -schema['title']._validationLayer() - - -class SRTemplate(BaseContent): - implements(ISamplingRoundTemplate, IDeactivable) - security = ClassSecurityInfo() - schema = schema - displayContentsTab = False - - _at_rename_after_creation = True - def _renameAfterCreation(self, check_auto_id=False): - renameAfterCreation(self) - - def _getSamplersDisplayList(self): - """ Returns the available users in the system with the roles - 'LabManager' and/or 'Sampler' - """ - return getUsers(self, ['LabManager', 'Sampler']) - - def _getDepartmentsDisplayList(self): - """ Returns the available departments in the system. Only the - active departments are shown, unless the object has an - inactive department already assigned. - """ - bsc = getToolByName(self, 'bika_setup_catalog') - items = [('', '')] + [(o.UID, o.Title) for o in - bsc(portal_type='Department', - is_active=True)] - o = self.getDepartment() - if o and o.UID() not in [i[0] for i in items]: - items.append((o.UID(), o.Title())) - items.sort(lambda x, y: cmp(x[1], y[1])) - return DisplayList(list(items)) - - -registerType(SRTemplate, PROJECTNAME) diff --git a/bika/lims/controlpanel/bika_samplingrounds.py b/bika/lims/controlpanel/bika_samplingrounds.py deleted file mode 100644 index 3a31ad3856..0000000000 --- a/bika/lims/controlpanel/bika_samplingrounds.py +++ /dev/null @@ -1,116 +0,0 @@ -# -*- 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 bikaMessageFactory as _ -from bika.lims.browser.bika_listing import BikaListingView -from plone.dexterity.content import Container -from plone.supermodel import model -from zope.interface import implements - - -class SamplingRoundsView(BikaListingView): - """Displays all system's sampling rounds - """ - - def __init__(self, context, request): - super(SamplingRoundsView, self).__init__(context, request) - self.catalog = "portal_catalog" - self.contentFilter = { - 'portal_type': 'SamplingRound', - 'sort_on': 'sortable_title' - } - - self.show_select_row = False - self.show_select_column = True - self.pagesize = 25 - self.form_id = "samplinground" - self.icon = self.portal_url + "/++resource++bika.lims.images/instrumentcertification_big.png" - self.title = self.context.translate(_("Sampling Rounds")) - self.description = "" - # Hide the ugly edit-bar with 'new', 'draft', etc - # self.request.set('disable_border', 1) - self.columns = { - 'title': {'title': _('Title'), - 'sortable': True, - 'toggle': True, - 'replace_url': 'absolute_url'}, - 'Description': {'title': _('Description')}, - 'num_sample_points': {'title': _('Number of sampling points'), - 'index': 'sortable_title'}, - 'num_containers': {'title': _('Number of containers'), - 'index': 'sortable_title'}, - } - self.review_states = [ - {'id': 'default', - 'title': _('Open'), - 'contentFilter': {'review_state': 'open'}, - 'columns': ['title', - 'Description', - 'num_sample_points', - 'num_containers', - ] - }, - {'id': 'closed', - 'contentFilter': {'review_state': 'closed'}, - 'title': _('Closed'), - 'transitions': [{'id': 'open'}], - 'columns': ['title', - 'Description', - 'num_sample_points', - 'num_containers', - ] - }, - {'id': 'cancelled', - 'title': _('Cancelled'), - 'transitions': [{'id': 'reinstate'}], - 'contentFilter': {'review_state': 'cancelled'}, - 'columns': ['title', - 'Description', - 'num_sample_points', - 'num_containers', - ] - }, - {'id': 'all', - 'title': _('All'), - 'transitions': [], - 'contentFilter':{}, - 'columns': ['title', - 'Description', - 'num_sample_points', - 'num_containers', - ] - }, - ] - - def before_render(self): - """Before template render hook - """ - # Don't allow any context actions - self.request.set("disable_border", 1) - - -class ISamplingRounds(model.Schema): - """ A Sampling Rounds container. - """ - - -class SamplingRounds(Container): - implements(ISamplingRounds) - displayContentsTab = False diff --git a/bika/lims/controlpanel/bika_srtemplates.py b/bika/lims/controlpanel/bika_srtemplates.py deleted file mode 100644 index c99bbdaffa..0000000000 --- a/bika/lims/controlpanel/bika_srtemplates.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- 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 bikaMessageFactory as _ -from bika.lims.browser.bika_listing import BikaListingView -from bika.lims.config import PROJECTNAME -from bika.lims.interfaces import ISamplingRoundTemplates -from bika.lims.permissions import AddSRTemplate -from bika.lims.utils import checkPermissions -from plone.app.folder.folder import ATFolder -from plone.app.folder.folder import ATFolderSchema -from Products.Archetypes import atapi -from Products.ATContentTypes.content import schemata -from Products.CMFCore.permissions import AddPortalContent -from Products.CMFCore.permissions import ModifyPortalContent -from zope.interface.declarations import implements - - -class SamplingRoundTemplatesView(BikaListingView): - """ - Displays the list of Sampling Round Templates registered in the system. - For users with 'Bika: Add SRTemplate' permission granted (along with - ModifyPortalContent and AddPortalContent), an "Add" button will be - displayed at the top of the list. - """ - - def __init__(self, context, request): - super(SamplingRoundTemplatesView, self).__init__(context, request) - self.form_id = "srtemplates" - self.show_select_column = True - self.icon = self.portal_url + "/++resource++bika.lims.images/srtemplate_big.png" - self.title = self.context.translate(_("Sampling Round Templates")) - self.catalog = "bika_setup_catalog" - self.contentFilter = { - 'portal_type': 'SRTemplate', - 'sort_order': 'sortable_title', - 'path': { - "query": "/".join(self.context.getPhysicalPath()), - "level": 0 - }, - } - self.columns = { - 'Title': { - 'title': _('Template'), - 'index': 'sortable_title', - 'replace_url': 'absolute_url' - }, - 'Description': { - 'title': _('Description'), - 'index': 'description' - }, - } - self.review_states = [ - {'id':'default', - 'title': _('Active'), - 'contentFilter': {'is_active': True}, - 'columns': ['Title', - 'Description']}, - {'id':'inactive', - 'title': _('Inactive'), - 'contentFilter': {'is_active': False}, - 'columns': ['Title', - 'Description']}, - {'id':'all', - 'title': _('All'), - 'contentFilter':{}, - 'columns': ['Title', - 'Description']}, - ] - - def __call__(self): - # Has the current user (might be a Client's contact) enough - # privileges to add a Sampling Round Template?. This check must be done - # here in the __call__ function because the user (and checkpermission) - # is only accessible once the object has already been instantiated. - reqperms = [ModifyPortalContent, AddPortalContent, AddSRTemplate] - if checkPermissions(reqperms, self.context): - self.context_actions = { - _('Add'): { - 'url': 'createObject?type_name=SRTemplate', - 'permission': AddSRTemplate, - 'icon': '++resource++bika.lims.images/add.png' - } - } - return super(SamplingRoundTemplatesView, self).__call__() - - def before_render(self): - """Before template render hook - """ - # Don't allow any context actions - self.request.set("disable_border", 1) - - -schema = ATFolderSchema.copy() - - -class SRTemplates(ATFolder): - implements(ISamplingRoundTemplates) - displayContentsTab = False - schema = schema - - -schemata.finalizeATCTSchema(schema, folderish = True, moveDiscussion = False) -atapi.registerType(SRTemplates, PROJECTNAME) diff --git a/bika/lims/controlpanel/configure.zcml b/bika/lims/controlpanel/configure.zcml index 3bdb4ef6e1..817fec7378 100644 --- a/bika/lims/controlpanel/configure.zcml +++ b/bika/lims/controlpanel/configure.zcml @@ -212,14 +212,6 @@ layer="bika.lims.interfaces.IBikaLIMS" /> - - - - - - @@ -91,10 +89,6 @@ # Transition Permissions (Supply Order) - # Transition Permissions (Sampling Round) - - - # Transition Permissions (Worksheet) @@ -132,7 +126,6 @@ - diff --git a/bika/lims/profiles/default/catalog.xml b/bika/lims/profiles/default/catalog.xml index 68dd0bfe5d..2b6cd252c0 100644 --- a/bika/lims/profiles/default/catalog.xml +++ b/bika/lims/profiles/default/catalog.xml @@ -9,10 +9,6 @@ - - - -
diff --git a/bika/lims/profiles/default/controlpanel.xml b/bika/lims/profiles/default/controlpanel.xml index ce29a60a5d..dbdc1f9c41 100644 --- a/bika/lims/profiles/default/controlpanel.xml +++ b/bika/lims/profiles/default/controlpanel.xml @@ -275,15 +275,6 @@ senaite.core: Manage Bika - - senaite.core: Manage Bika - - senaite.core: Manage Bika - - senaite.core: Manage Bika - - - diff --git a/bika/lims/profiles/default/jsregistry.xml b/bika/lims/profiles/default/jsregistry.xml index 024f6f2f4b..ced42fc731 100644 --- a/bika/lims/profiles/default/jsregistry.xml +++ b/bika/lims/profiles/default/jsregistry.xml @@ -373,28 +373,6 @@ inline="False" insert-after="*"/> - - - - - @@ -101,7 +100,6 @@ - diff --git a/bika/lims/profiles/default/rolemap.xml b/bika/lims/profiles/default/rolemap.xml index f9eaef33e7..54d11d83dd 100644 --- a/bika/lims/profiles/default/rolemap.xml +++ b/bika/lims/profiles/default/rolemap.xml @@ -330,20 +330,6 @@ - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/structure/.preserve b/bika/lims/profiles/default/structure/.preserve index cf8da73b00..0ffc0f78c9 100644 --- a/bika/lims/profiles/default/structure/.preserve +++ b/bika/lims/profiles/default/structure/.preserve @@ -8,4 +8,3 @@ methods pricelists bika_setup reports -samplingrounds diff --git a/bika/lims/profiles/default/structure/bika_setup/.objects b/bika/lims/profiles/default/structure/bika_setup/.objects index 06bc8e3f7a..cf076fb8b3 100644 --- a/bika/lims/profiles/default/structure/bika_setup/.objects +++ b/bika/lims/profiles/default/structure/bika_setup/.objects @@ -26,8 +26,6 @@ bika_samplepoints,SamplePoints bika_sampletypes,SampleTypes bika_storagelocations,StorageLocations bika_samplingdeviations,SamplingDeviations -bika_samplingrounds,SamplingRounds -bika_srtemplates,SRTemplates bika_subgroups,SubGroups bika_suppliers,Suppliers bika_worksheettemplates,WorksheetTemplates diff --git a/bika/lims/profiles/default/structure/bika_setup/.preserve b/bika/lims/profiles/default/structure/bika_setup/.preserve index e2ec2f2431..42475a2739 100644 --- a/bika/lims/profiles/default/structure/bika_setup/.preserve +++ b/bika/lims/profiles/default/structure/bika_setup/.preserve @@ -25,8 +25,6 @@ bika_samplematrices bika_samplepoints bika_sampletypes bika_samplingdeviations -bika_samplingrounds -bika_srtemplates bika_storagelocations bika_suppliers bika_subgroups diff --git a/bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.object b/bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.object deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.properties b/bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.properties deleted file mode 100644 index e43db52bbe..0000000000 --- a/bika/lims/profiles/default/structure/bika_setup/bika_samplingrounds/.properties +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -description = -title = Sampling Rounds - diff --git a/bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.objects b/bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.objects deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.properties b/bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.properties deleted file mode 100644 index 1bf63f9739..0000000000 --- a/bika/lims/profiles/default/structure/bika_setup/bika_srtemplates/.properties +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -description = -title = Sampling Round Templates diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index cae6bde25e..4761f39118 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -88,10 +88,6 @@ - - - - diff --git a/bika/lims/profiles/default/types/Client.xml b/bika/lims/profiles/default/types/Client.xml index ec6dca0c6b..6a6e251d04 100644 --- a/bika/lims/profiles/default/types/Client.xml +++ b/bika/lims/profiles/default/types/Client.xml @@ -26,10 +26,8 @@ - - False @@ -145,30 +143,6 @@ - - - - - - - - - - Sampling Round Template - - ++resource++bika.lims.images/srtemplate.png - SRTemplate - bika.lims - addSRTemplate - - - False - True - - False - False - artemplates - - - - - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/types/SRTemplates.xml b/bika/lims/profiles/default/types/SRTemplates.xml deleted file mode 100644 index 9e0492df17..0000000000 --- a/bika/lims/profiles/default/types/SRTemplates.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - Sampling Round Templates - - ++resource++bika.lims.images/srtemplate.png - SRTemplates - bika.lims - addSRTemplates - - - False - True - - - - False - False - - - - - - - diff --git a/bika/lims/profiles/default/types/SamplingRound.xml b/bika/lims/profiles/default/types/SamplingRound.xml deleted file mode 100644 index 7961863c67..0000000000 --- a/bika/lims/profiles/default/types/SamplingRound.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Sampling Round - A sampling round object - string:${portal_url}/document_icon.png - bika.lims.content.samplingsround - False - True - - - - - False - - - bika.lims.content.samplinground.ISamplingRound - bika.lims.content.samplinground.SamplingRound - - - - - - - - analysisrequests - view - - - - False - cmf.AddPortalContent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/types/SamplingRounds.xml b/bika/lims/profiles/default/types/SamplingRounds.xml deleted file mode 100644 index dd50f89232..0000000000 --- a/bika/lims/profiles/default/types/SamplingRounds.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Sampling Rounds - Show all the sampling rounds registered in the system - string:${portal_url}/folder_icon.png - bika.lims.controlpanel.bika_samplingrounds - True - True - - - - False - - - bika.lims.controlpanel.bika_samplingrounds.ISamplingRounds - bika.lims.controlpanel.bika_samplingrounds.SamplingRounds - - - - - - - - view - view - - - - False - cmf.AddPortalContent - - - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/workflows.xml b/bika/lims/profiles/default/workflows.xml index e27d2369a9..544068e9ee 100644 --- a/bika/lims/profiles/default/workflows.xml +++ b/bika/lims/profiles/default/workflows.xml @@ -10,7 +10,6 @@ - @@ -252,12 +251,6 @@ - - - - - - @@ -382,12 +375,6 @@ - - - - - - diff --git a/bika/lims/profiles/default/workflows/bika_samplinground_workflow/definition.xml b/bika/lims/profiles/default/workflows/bika_samplinground_workflow/definition.xml deleted file mode 100644 index 3a4df5349e..0000000000 --- a/bika/lims/profiles/default/workflows/bika_samplinground_workflow/definition.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - Add portal content - senaite.core: Add AnalysisRequest - senaite.core: Transition: Close Sampling Round - senaite.core: Transition: Reopen Sampling Round - Modify portal content - View - - - The SamplingRound can't be edited and Samples cannot be attached - - - - - - - - - - - Analyst - Client - LabClerk - LabManager - Manager - Owner - RegulatoryInspector - Verifier - - - - - The Sampling Round can be edited and Samples can be attached - - - Client - LabClerk - LabManager - Manager - Owner - - - Client - LabClerk - LabManager - Manager - Owner - - - - - Client - LabClerk - LabManager - Manager - Owner - - - Analyst - Client - LabClerk - LabManager - Manager - Owner - RegulatoryInspector - Verifier - - - - - Close - - senaite.core: Transition: Close Sampling Round - python:here.workflow_guard_close() if hasattr(here, 'workflow_guard_close') else True - - - - - Open - - senaite.core: Transition: Reopen Sampling Round - python:here.workflow_guard_open() if hasattr(here, 'workflow_guard_open') else True - - - - - Previous transition - - transition/getId|nothing - - - - - - - The ID of the user who performed the last transition - - user/getId - - - - - - - Comment about the last transition - - python:state_change.kwargs.get('comment', '') - - - - - - - Provides access to workflow history - - state_change/getHistory - - - - - - - When the previous transition was performed - - state_change/getDateTime - - - - - - diff --git a/bika/lims/setuphandlers.py b/bika/lims/setuphandlers.py index c84f37c0ed..9f06f4597c 100644 --- a/bika/lims/setuphandlers.py +++ b/bika/lims/setuphandlers.py @@ -116,7 +116,6 @@ ("Preservation", ["bika_setup_catalog"]), ("ReferenceDefinition", ["bika_setup_catalog", "portal_catalog"]), ("ReferenceSample", ["bika_catalog", "portal_catalog"]), - ("SRTemplate", ["bika_setup_catalog", "portal_catalog"]), ("SampleCondition", ["bika_setup_catalog"]), ("SampleMatrix", ["bika_setup_catalog"]), ("SamplePoint", ["bika_setup_catalog", "portal_catalog"]), diff --git a/bika/lims/subscribers/configure.zcml b/bika/lims/subscribers/configure.zcml index 16bf26c8c4..292673f9e0 100644 --- a/bika/lims/subscribers/configure.zcml +++ b/bika/lims/subscribers/configure.zcml @@ -79,12 +79,6 @@ handler="bika.lims.subscribers.analysis.ObjectRemovedEventHandler" /> - - Date: Tue, 11 Feb 2020 15:44:11 +0100 Subject: [PATCH 05/28] Changelog updated --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 74e86cca32..9a681da875 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -38,6 +38,7 @@ Changelog **Removed** +- #1530 Removed stale type registrations - #1518 Removed stale indexes from `analysis_catalog` - #1516 Removed getResultsRange metadata from analysis_catalog - #1487 Dexterity Compatible Catalog Base Class From d0f48e56a0320b31f4cd92cfb77f231a5c84b4d0 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 15:45:07 +0100 Subject: [PATCH 06/28] Changelog updated --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9a681da875..f2b220b36b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -38,6 +38,7 @@ Changelog **Removed** +- #1530 Remove sampling rounds from core - #1530 Removed stale type registrations - #1518 Removed stale indexes from `analysis_catalog` - #1516 Removed getResultsRange metadata from analysis_catalog From a953eaab4ee7cf5ebafa73f97dbee715d820cf78 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 15:45:48 +0100 Subject: [PATCH 07/28] Added link in upgrade step to PR --- bika/lims/upgrade/v01_03_003.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 09376d030d..e2801d6639 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -362,6 +362,7 @@ def upgrade(tool): remove_stale_type_registrations(portal) # remove samplingrounds et.al + # https://github.com/senaite/senaite.core/pull/1531 remove_samplingrounds(portal) logger.info("{0} upgraded to version {1}".format(product, version)) From 9636b70b945b1d4ef4ce02c9a6d7b133e93348f0 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 15:46:29 +0100 Subject: [PATCH 08/28] Added link to PR in upgrade step --- bika/lims/upgrade/v01_03_003.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index c6daa96d1c..fd03526913 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -359,6 +359,7 @@ def upgrade(tool): remove_stale_javascripts(portal) # remove stale type regsitrations + # https://github.com/senaite/senaite.core/pull/1530 remove_stale_type_registrations(portal) logger.info("{0} upgraded to version {1}".format(product, version)) From 108c072a8203cc9779636a87b4d7e35662fee644 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 15:54:42 +0100 Subject: [PATCH 09/28] Removed remaining permissions and icons --- bika/lims/browser/images/srtemplate.png | Bin 569 -> 0 bytes bika/lims/browser/images/srtemplate_big.png | Bin 1173 -> 0 bytes bika/lims/profiles/default/rolemap.xml | 16 ---------------- 3 files changed, 16 deletions(-) delete mode 100644 bika/lims/browser/images/srtemplate.png delete mode 100644 bika/lims/browser/images/srtemplate_big.png diff --git a/bika/lims/browser/images/srtemplate.png b/bika/lims/browser/images/srtemplate.png deleted file mode 100644 index 9ee64c26c529fba625fff40981b92e1fba15f487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 569 zcmV-90>=G`P)q$gGR5;6(PdjKD0T}*HP7tR<3Kpj$_s-s}_7Lhq&0m6Kt{9C3a{cCg1e)z4(4$ z4|twG2A}{?uJ2o+0su*p9@c8LKqixUQz#T(?=z1^BV#-s&#l*M&5k2uu~^PvFvNC5 zHk(cCE11n@bgR{RDoK(7z!(6q*=$Ig&33h$+iW(U9eP0&MQ<{hd=CJse!uS=33$w6U>K$fpgNz=kH|)& z@tk3p3ynte3&822@D90LF6Q_9Ps!2Nr-+;>5}UVo8DB(jJ^B6)|yae3bY09Y&*576m!yaYjPE0xNt zy9TS(>PxrV{dKuqQUHK(IDAe~)Uwy>IS+-m+pTIm9@FV``t3hbmSwHa=le`i)UsSI z?_I+4{IS_=9twi+_=YIUvX*7pbAlkYcDwzGGe0)as8+S=Mj zUa$ATZ6>E_8ns%jrt|aj?Dvg9xrd{pqkMuO9uowC69l0n2;#9+DlK7I790-8BLH}F zQv!p7gD)m0Crvh+t;gf>w3U^W?I#-aRepZ{-*9P9xh4{c!Zb~ze4AuqfFwzoLZNue z^_$J+7YSLCBq`HswST;iKxJj+Zqr=l#+SDxa2YgB zqeDYOU!I+veeg~UU>N2zo6SFNZ*OZa8|ieqt()$BB2W~S)6~@TYI%A26Tjat1%Uo| z=l=eFiBKrKNTk5V#>R)KP~i0RG`FCj;4J{K2!cdiE|&@*b_^{R%jYyr3jqK|M@NUW zTJ1IffIuLKaL;#zR4NTxt=0j(UjNI|($dFjwb~t*CMzo|1ORMNQPCky(@R20>#AdS@%w}^c1h|5gm6bY{WfAD@?VaXU zvAerlo{aHb6kr$zt*WZp006eLv-5j|AP5`)Kqiy`W(z0A0GBYzHKA%qp0N`*q zaP;u-uowWKyu5sGeSJOM{s91>ySw{4r_=dJEEfNZQWTX70FalLcX}`00bpcgWGw!I zAV?Gd*p`+S`+X7M1C>Z5!9XA&_51y@`*J!yKK|U{aOhAJrMXngViTVC8c8$?@?qmTKn|MdcJ07XAeQY(_>#l*_-*-)fKVuu9lL&60AM>hI$ouM1j8^WKd$U{d+R&$^Wk>8OJy?I zKYS(bvK>C3PukGXu-M$(`~$xZycSzqTR5MDTrNMEo}M-^41*>$K}_zsxw&Twg(6<= z*Vfj)PMW#f?Jm`5G=Bo`zE>)h9*f0dzAjlE92}HPOiY;6YV|JPR;5z8oKEM%8_5?6 zg|Y_*1}3=E!EYFbom5v>|JvHxYHw?6v)9zryiW9UBM=Cf{{H^2la@abTrQWYr>AF{ zs~eMPB9SQE)zvk-v$ONRtIkN4NH7@8SzKIfTwPs#LJ$P*^?Hkg!C)@SvLKO2&WekR ny*Q3L^m_eTV`Jk&vh~G(u8K7A3DFYZ00000NkvXXu0mjfz#tf< diff --git a/bika/lims/profiles/default/rolemap.xml b/bika/lims/profiles/default/rolemap.xml index 54d11d83dd..70502d4e57 100644 --- a/bika/lims/profiles/default/rolemap.xml +++ b/bika/lims/profiles/default/rolemap.xml @@ -493,22 +493,6 @@ - - - - - - - - - - - - - - - - From ece8ebbb896f0e8808fabf29d1a445d20d13aefd Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 16:11:56 +0100 Subject: [PATCH 10/28] Removed permission senaite.core: Field: Edit Sampling Round --- bika/lims/profiles/default/rolemap.xml | 5 ----- .../workflows/bika_ar_workflow/definition.xml | 14 -------------- 2 files changed, 19 deletions(-) diff --git a/bika/lims/profiles/default/rolemap.xml b/bika/lims/profiles/default/rolemap.xml index 70502d4e57..724f7a3437 100644 --- a/bika/lims/profiles/default/rolemap.xml +++ b/bika/lims/profiles/default/rolemap.xml @@ -676,11 +676,6 @@ - - - - - diff --git a/bika/lims/profiles/default/workflows/bika_ar_workflow/definition.xml b/bika/lims/profiles/default/workflows/bika_ar_workflow/definition.xml index 6920fcfd02..637a56bfb2 100644 --- a/bika/lims/profiles/default/workflows/bika_ar_workflow/definition.xml +++ b/bika/lims/profiles/default/workflows/bika_ar_workflow/definition.xml @@ -74,7 +74,6 @@ senaite.core: Field: Edit Sampler senaite.core: Field: Edit Sampling Date senaite.core: Field: Edit Sampling Deviation - senaite.core: Field: Edit Sampling Round senaite.core: Field: Edit Scheduled Sampler senaite.core: Field: Edit Specification senaite.core: Field: Edit Storage Location @@ -162,7 +161,6 @@ - @@ -255,7 +253,6 @@ - @@ -354,7 +351,6 @@ - @@ -443,7 +439,6 @@ - @@ -530,7 +525,6 @@ - @@ -613,7 +607,6 @@ - @@ -703,7 +696,6 @@ - @@ -789,7 +781,6 @@ - @@ -876,7 +867,6 @@ - @@ -953,7 +943,6 @@ - @@ -1028,7 +1017,6 @@ - @@ -1103,7 +1091,6 @@ - @@ -1183,7 +1170,6 @@ - From 9c820ac4d913492677f2c9f521dd71270f1052c1 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 17:41:42 +0100 Subject: [PATCH 11/28] Remove existing Sampling Rounds / SRTemplates --- bika/lims/upgrade/v01_03_003.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index c78809b70f..6db371273e 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -802,6 +802,20 @@ def remove_samplingrounds(portal): "++resource++bika.lims.js/bika.lims.samplingrounds.js"] wfs_to_remove = ["bika_samplinground_workflow"] + # 0. Remove all samplingrounds and srtemplates + pc = portal.portal_catalog + for brain in pc({"portal_type": "SamplingRound"}): + obj = brain.getObject() + if hasattr(obj, "unindexObject"): + obj.unindexObject() + obj.aq_parent._delOb(obj.id) + + for brain in pc({"portal_type": "SRTemplate"}): + obj = brain.getObject() + if hasattr(obj, "unindexObject"): + obj.unindexObject() + obj.aq_parent._delOb(obj.id) + # 1. Remove the identifiers and identifier types setup = portal.bika_setup try: @@ -810,7 +824,8 @@ def remove_samplingrounds(portal): parent = setup[oid] for obj in parent.objectValues(): obj.unindexObject() - parent.unindexObject() + if hasattr(parent, "unindexObject"): + parent.unindexObject() setup._delOb(oid) except KeyError: pass @@ -821,10 +836,10 @@ def remove_samplingrounds(portal): cp.unregisterConfiglet(oid) # 3. Remove catalog indexes - cat = portal.portal_catalog + pc = portal.portal_catalog for idx in idxs_to_remove: - if idx in cat.indexes(): - cat.manage_delIndex(idx) + if idx in pc.indexes(): + pc.manage_delIndex(idx) # 4. Remove type registration pt = portal.portal_types From 303430eca35610f656c73c34d404555f5fdd025f Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 21:38:31 +0100 Subject: [PATCH 12/28] Upgrade step improved --- bika/lims/upgrade/v01_03_003.py | 50 +++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 6db371273e..941d3a66d4 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -797,63 +797,95 @@ def remove_samplingrounds(portal): "SRTemplate", "SRTemplates"] objs_to_remove = ["bika_srtemplates", "bika_samplingrounds"] idxs_to_remove = ["SamplingRoundUID", "samplingRoundSamplingDate"] + columns_to_remove = ["SamplingRoundUID", "samplingRoundSamplingDate"] js_to_remove = [ "++resource++bika.lims.js/bika.lims.samplinground.print.js", "++resource++bika.lims.js/bika.lims.samplingrounds.js"] wfs_to_remove = ["bika_samplinground_workflow"] - # 0. Remove all samplingrounds and srtemplates + def remove_actions(action_ids, obj): + type_info = obj.getTypeInfo() + actions = map(lambda action: action.id, type_info._actions) + for index, action in enumerate(actions, start=0): + if action in action_ids: + type_info.deleteActions([index]) + + # Remove all samplingrounds pc = portal.portal_catalog for brain in pc({"portal_type": "SamplingRound"}): obj = brain.getObject() if hasattr(obj, "unindexObject"): obj.unindexObject() + logger.info("Removing object '{}'".format(api.get_path(obj))) obj.aq_parent._delOb(obj.id) + # Remove all samplinground templates for brain in pc({"portal_type": "SRTemplate"}): obj = brain.getObject() if hasattr(obj, "unindexObject"): obj.unindexObject() + logger.info("Removing object '{}'".format(api.get_path(obj))) obj.aq_parent._delOb(obj.id) - # 1. Remove the identifiers and identifier types + # Remove actions from clients + for client in portal.clients.objectValues(): + logger.info("Removing actions for '{}'".format(api.get_path(client))) + remove_actions(["sampling_rounds_view", "srtemplates"], client) + + # Remove the setup objects setup = portal.bika_setup try: # we use _delOb because manage_delObjects raises an unauthorized here for oid in objs_to_remove: parent = setup[oid] + # remove contained objects for obj in parent.objectValues(): - obj.unindexObject() + if hasattr(obj, "unindexObject"): + obj.unindexObject() + # remove parent objects if hasattr(parent, "unindexObject"): parent.unindexObject() + logger.info("Removing object '{}'".format(api.get_path(obj))) setup._delOb(oid) except KeyError: pass - # 2. Remove controlpanel configlet + # Remove controlpanel configlet cp = portal.portal_controlpanel for oid in objs_to_remove: + logger.info("Removing configlet '{}'".format(oid)) cp.unregisterConfiglet(oid) - # 3. Remove catalog indexes + # Remove catalog indexes pc = portal.portal_catalog for idx in idxs_to_remove: if idx in pc.indexes(): + logger.info("Removing catalog index '{}'".format(idx)) pc.manage_delIndex(idx) - # 4. Remove type registration + # Remove catalog metadata + for column in columns_to_remove: + if column in pc.schema(): + logger.info("Removing catalog column '{}'".format(column)) + pc.delColumn(column) + + # Remove portal_type registration pt = portal.portal_types for t in types_to_remove: if t in pt.objectIds(): + logger.info("Removing portal type '{}'".format(t)) pt.manage_delObjects(t) - # 5. Remove javascripts + # Remove javascripts for js in js_to_remove: + logger.info("Removing JavaScript '{}'".format(js)) portal.portal_javascripts.unregisterResource(js) - # 6. Remove Workflows + # Remove Workflows wf_tool = portal.portal_workflow for wf in wfs_to_remove: - wf_tool.manage_delObjects(wf) + if wf in wf_tool: + logger.info("Removing Workflow '{}'".format(wf)) + wf_tool.manage_delObjects(wf) logger.info("Removing samplingrounds [DONE]") From f7cb21a87dc193b624e956c4bde9a9ed3d4192a6 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Tue, 11 Feb 2020 22:06:05 +0100 Subject: [PATCH 13/28] Deleted template --- .../srtemplateartemplateswidget.pt | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 bika/lims/skins/bika/bika_widgets/srtemplateartemplateswidget.pt diff --git a/bika/lims/skins/bika/bika_widgets/srtemplateartemplateswidget.pt b/bika/lims/skins/bika/bika_widgets/srtemplateartemplateswidget.pt deleted file mode 100644 index ccb088cfa4..0000000000 --- a/bika/lims/skins/bika/bika_widgets/srtemplateartemplateswidget.pt +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - -
- - -
-
- -
-
- - - From d4935991de00ffe04f1e044266e8129793c63c4d Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 19:44:07 +0100 Subject: [PATCH 14/28] Ressurected Reject Analysis --- bika/lims/__init__.py | 1 + bika/lims/content/rejectanalysis.py | 50 +++++++++++++++++++++++++++++ bika/lims/upgrade/v01_03_003.py | 1 - 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 bika/lims/content/rejectanalysis.py diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index 7075356cfb..e1f0d547d9 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -119,6 +119,7 @@ def initialize(context): from content.referencesample import ReferenceSample # noqa from content.referencesamplesfolder import ReferenceSamplesFolder # noqa from content.reflexrule import ReflexRule # noqa + from content.rejectanalysis import RejectAnalysis # noqa from content.report import Report # noqa from content.reportfolder import ReportFolder # noqa from content.sample import Sample # noqa diff --git a/bika/lims/content/rejectanalysis.py b/bika/lims/content/rejectanalysis.py new file mode 100644 index 0000000000..0cd0a90ae7 --- /dev/null +++ b/bika/lims/content/rejectanalysis.py @@ -0,0 +1,50 @@ +# -*- 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 AccessControl import ClassSecurityInfo +from Products.Archetypes.public import ReferenceField, Schema, registerType +from bika.lims.content.analysis import Analysis +from bika.lims.config import PROJECTNAME +from bika.lims.content.analysis import schema as analysis_schema +from bika.lims.interfaces import IRejectAnalysis +from zope.interface import implements + +schema = analysis_schema + Schema(( + # The analysis that was originally rejected + ReferenceField( + 'Analysis', + allowed_types=('Analysis',), + relationship='RejectAnalysisAnalysis', + ), +)) + + +class RejectAnalysis(Analysis): + implements(IRejectAnalysis) + security = ClassSecurityInfo() + + schema = schema + + @security.public + def getSample(self): + return self.aq_parent + + +registerType(RejectAnalysis, PROJECTNAME) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index fd03526913..6b118c6123 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -44,7 +44,6 @@ TYPES_TO_REMOVE = [ "BikaCache", - "RejectAnalysis", # invoices were removed in upgrade step 1.3.0 "InvoiceBatch", "InvoiceFolder", From 874af00c907ff569ae4483bab9ff1d128aaa2242 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 20:49:32 +0100 Subject: [PATCH 15/28] Don't remove existing samplingrounds and srtemplates in upgrade step --- bika/lims/upgrade/v01_03_003.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 526daeb680..01dfec6e31 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -796,7 +796,7 @@ def remove_samplingrounds(portal): types_to_remove = ["SamplingRound", "SamplingRounds", "SRTemplate", "SRTemplates"] - objs_to_remove = ["bika_srtemplates", "bika_samplingrounds"] + ids_to_remove = ["bika_srtemplates", "bika_samplingrounds"] idxs_to_remove = ["SamplingRoundUID", "samplingRoundSamplingDate"] columns_to_remove = ["SamplingRoundUID", "samplingRoundSamplingDate"] js_to_remove = [ @@ -811,23 +811,6 @@ def remove_actions(action_ids, obj): if action in action_ids: type_info.deleteActions([index]) - # Remove all samplingrounds - pc = portal.portal_catalog - for brain in pc({"portal_type": "SamplingRound"}): - obj = brain.getObject() - if hasattr(obj, "unindexObject"): - obj.unindexObject() - logger.info("Removing object '{}'".format(api.get_path(obj))) - obj.aq_parent._delOb(obj.id) - - # Remove all samplinground templates - for brain in pc({"portal_type": "SRTemplate"}): - obj = brain.getObject() - if hasattr(obj, "unindexObject"): - obj.unindexObject() - logger.info("Removing object '{}'".format(api.get_path(obj))) - obj.aq_parent._delOb(obj.id) - # Remove actions from clients for client in portal.clients.objectValues(): logger.info("Removing actions for '{}'".format(api.get_path(client))) @@ -837,7 +820,7 @@ def remove_actions(action_ids, obj): setup = portal.bika_setup try: # we use _delOb because manage_delObjects raises an unauthorized here - for oid in objs_to_remove: + for oid in ids_to_remove: parent = setup[oid] # remove contained objects for obj in parent.objectValues(): @@ -853,7 +836,7 @@ def remove_actions(action_ids, obj): # Remove controlpanel configlet cp = portal.portal_controlpanel - for oid in objs_to_remove: + for oid in ids_to_remove: logger.info("Removing configlet '{}'".format(oid)) cp.unregisterConfiglet(oid) From 5e693f0fbee6efbf44100e8d98df818a051c7046 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 20:56:00 +0100 Subject: [PATCH 16/28] Removed Sample Content Class --- bika/lims/content/sample.py | 481 ------------------------------- bika/lims/interfaces/__init__.py | 5 - 2 files changed, 486 deletions(-) delete mode 100644 bika/lims/content/sample.py diff --git a/bika/lims/content/sample.py b/bika/lims/content/sample.py deleted file mode 100644 index 00cdc80010..0000000000 --- a/bika/lims/content/sample.py +++ /dev/null @@ -1,481 +0,0 @@ -# -*- 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. - -"""Sample represents a physical sample submitted for testing -""" - -from datetime import timedelta -from AccessControl import ClassSecurityInfo -from bika.lims import bikaMessageFactory as _ -from bika.lims.api import get_object_by_uid -from bika.lims.browser.fields.remarksfield import RemarksField -from bika.lims.browser.fields.uidreferencefield import get_backreferences -from bika.lims.utils import t, getUsers -from Products.ATExtensions.field import RecordsField -from bika.lims.browser.widgets.datetimewidget import DateTimeWidget -from bika.lims.browser.widgets import RejectionWidget -from bika.lims.browser.widgets import RemarksWidget -from bika.lims.config import PROJECTNAME -from bika.lims.content.bikaschema import BikaSchema -from bika.lims.interfaces import ISample -from Products.Archetypes import atapi -from Products.Archetypes.public import * -from Products.Archetypes.references import HoldingReference -from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin -from Products.ATContentTypes.utils import DT2dt, dt2DT -from Products.CMFCore import permissions -from Products.CMFPlone.utils import safe_unicode -from zope.interface import implements - -from bika.lims.browser.fields import DateTimeField -from bika.lims.browser.widgets import ReferenceWidget -from bika.lims.browser.widgets import SelectionWidget as BikaSelectionWidget - -import sys -from bika.lims.utils import to_unicode -from bika.lims.interfaces import IDoNotSupportSnapshots - -schema = BikaSchema.copy() + Schema(( - # TODO This field is only for v1.3.0 migration purposes - # bika_catalog contains an "isValid" index. We will take advantage of this - # index to keep track of the Samples that have been migrated already in - # order to prevent an unnecessary reimport when v1.3.0 is rerun. - # This field is used by `isValid` function - BooleanField('Migrated', - default = False, - ), - StringField('SampleID', - required=1, - searchable=True, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=StringWidget( - label=_("Sample ID"), - description=_("The ID assigned to the client's sample by the lab"), - visible=False, - render_own_label=True, - ), - ), - StringField('ClientReference', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=StringWidget( - label=_("Client Reference"), - visible=False, - render_own_label=True, - ), - ), - StringField('ClientSampleID', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=StringWidget( - label=_("Client SID"), - visible=False, - render_own_label=True, - ), - ), - ReferenceField('SampleType', - required=1, - vocabulary_display_path_bound=sys.maxsize, - allowed_types=('SampleType',), - relationship='SampleSampleType', - referenceClass=HoldingReference, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=ReferenceWidget( - label=_("Sample Type"), - render_own_label=True, - visible=False, - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - showOn=True, - ), - ), - ComputedField('SampleTypeTitle', - expression="here.getSampleType() and here.getSampleType().Title() or ''", - widget=ComputedWidget( - visible=False, - ), - ), - ReferenceField('SamplePoint', - vocabulary_display_path_bound=sys.maxsize, - allowed_types=('SamplePoint',), - relationship = 'SampleSamplePoint', - referenceClass = HoldingReference, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=ReferenceWidget( - label=_("Sample Point"), - render_own_label=True, - visible=False, - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - showOn=True, - ), - ), - ComputedField('SamplePointTitle', - expression = "here.getSamplePoint() and here.getSamplePoint().Title() or ''", - widget = ComputedWidget( - visible=False, - ), - ), - ReferenceField( - 'StorageLocation', - allowed_types='StorageLocation', - relationship='AnalysisRequestStorageLocation', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=ReferenceWidget( - label=_("Storage Location"), - description=_("Location where sample is kept"), - size=20, - render_own_label=True, - visible=False, - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - showOn=True, - ), - ), - BooleanField('SamplingWorkflowEnabled', - default_method='getSamplingWorkflowEnabledDefault' - ), - DateTimeField('DateSampled', - mode="rw", - read_permission=permissions.View, - widget = DateTimeWidget( - label=_("Date Sampled"), - show_time=True, - size=20, - visible=False, - render_own_label=True, - ), - ), - StringField('Sampler', - mode="rw", - read_permission=permissions.View, - vocabulary='getSamplers', - widget=BikaSelectionWidget( - format='select', - label=_("Sampler"), - visible=False, - render_own_label=True, - ), - ), - StringField('ScheduledSamplingSampler', - mode="rw", - read_permission=permissions.View, - vocabulary='getSamplers', - widget=BikaSelectionWidget( - description=_("Define the sampler supposed to do the sample in " - "the scheduled date"), - format='select', - label=_("Sampler for scheduled sampling"), - visible=False, - render_own_label=True, - ), - ), - DateTimeField('SamplingDate', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget = DateTimeWidget( - label=_("Expected Sampling Date"), - description=_("Define when the sampler has to take the samples"), - show_time=True, - visible=False, - render_own_label=True, - ), - ), - ReferenceField('SamplingDeviation', - vocabulary_display_path_bound = sys.maxsize, - allowed_types = ('SamplingDeviation',), - relationship = 'SampleSamplingDeviation', - referenceClass = HoldingReference, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=ReferenceWidget( - label=_("Sampling Deviation"), - render_own_label=True, - visible=False, - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - showOn=True, - ), - ), - ReferenceField('SampleCondition', - vocabulary_display_path_bound = sys.maxsize, - allowed_types = ('SampleCondition',), - relationship = 'SampleSampleCondition', - referenceClass = HoldingReference, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=ReferenceWidget( - label=_("Sample Condition"), - render_own_label=True, - visible=False, - catalog_name='bika_setup_catalog', - base_query={'is_active': True}, - showOn=True, - ), - ), - StringField( - 'EnvironmentalConditions', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=StringWidget( - label=_("Environmental Conditions"), - visible=False, - render_own_label=True, - size=20, - ), - ), - # Another way to obtain a transition date is using getTransitionDate - # function. We are using a DateTimeField/Widget here because in some - # cases the user may want to change the Received Date. - # AnalysisRequest and Sample's DateReceived fields needn't to have - # the same value. - # This field is updated in workflow_script_receive method. - DateTimeField('DateReceived', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget = DateTimeWidget( - label=_("Date Received"), - show_time=True, - datepicker_nofuture=1, - visible=False, - render_own_label=True, - ), - ), - ComputedField('ClientUID', - expression = 'context.aq_parent.UID()', - widget = ComputedWidget( - visible=False, - ), - ), - ComputedField('SampleTypeUID', - expression='context.getSampleType() and \ - context.getSampleType().UID() or None', - widget=ComputedWidget( - visible=False, - ), - ), - ComputedField('SamplePointUID', - expression = 'context.getSamplePoint() and context.getSamplePoint().UID() or None', - widget = ComputedWidget( - visible=False, - ), - ), - BooleanField('Composite', - default = False, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget = BooleanWidget( - label=_("Composite"), - visible=False, - render_own_label=True, - ), - ), - DateTimeField('DateExpired', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget = DateTimeWidget( - label=_("Date Expired"), - visible=False, - render_own_label=True, - ), - ), - ComputedField('DisposalDate', - expression = 'context.disposal_date()', - widget=DateTimeWidget( - visible=False, - render_own_label=True, - ), - ), - DateTimeField('DateDisposed', - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget = DateTimeWidget( - label=_("Date Disposed"), - visible=False, - render_own_label=True, - ), - ), - BooleanField('AdHoc', - default=False, - mode="rw", - read_permission=permissions.View, - write_permission=permissions.ModifyPortalContent, - widget=BooleanWidget( - label=_("Ad-Hoc"), - visible=False, - render_own_label=True, - ), - ), - RemarksField( - 'Remarks', - widget=RemarksWidget( - label=_("Remarks"), - ), - ), - RecordsField( - 'RejectionReasons', - widget = RejectionWidget( - label=_("Sample Rejection"), - description = _("Set the Sample Rejection workflow and the reasons"), - render_own_label=False, - visible=False, - ), - ), -)) - - -schema['title'].required = False - - -class Sample(BaseFolder, HistoryAwareMixin): - implements(ISample, IDoNotSupportSnapshots) - security = ClassSecurityInfo() - displayContentsTab = False - schema = schema - - _at_rename_after_creation = True - - def _renameAfterCreation(self, check_auto_id=False): - from bika.lims.idserver import renameAfterCreation - renameAfterCreation(self) - - def _getCatalogTool(self): - from bika.lims.catalog import getCatalog - return getCatalog(self) - - def getSampleID(self): - """ Return the Sample ID as title """ - return safe_unicode(self.getId()).encode('utf-8') - - def Title(self): - """ Return the Sample ID as title """ - return self.getSampleID() - - def getSamplingWorkflowEnabledDefault(self): - return self.bika_setup.getSamplingWorkflowEnabled() - - def getContactTitle(self): - return "" - - def getClientTitle(self): - proxies = self.getAnalysisRequests() - if not proxies: - return "" - value = proxies[0].aq_parent.Title() - return value - - def getProfilesTitle(self): - return "" - - def getAnalysisService(self): - analyses = [] - for ar in self.getAnalysisRequests(): - analyses += list(ar.getAnalyses(full_objects=True)) - value = [] - for analysis in analyses: - val = analysis.Title() - if val not in value: - value.append(val) - return value - - def getAnalysts(self): - analyses = [] - for ar in self.getAnalysisRequests(): - analyses += list(ar.getAnalyses(full_objects=True)) - value = [] - for analysis in analyses: - val = analysis.getAnalyst() - if val not in value: - value.append(val) - return value - - security.declarePublic('getAnalysisRequests') - - def getAnalysisRequests(self): - backrefs = get_backreferences(self, 'AnalysisRequestSample') - ars = map(get_object_by_uid, backrefs) - return ars - - security.declarePublic('getAnalyses') - - def getAnalyses(self, contentFilter=None, **kwargs): - """ return list of all analyses against this sample - """ - # contentFilter and kwargs are combined. They both exist for - # compatibility between the two signatures; kwargs has been added - # to be compatible with how getAnalyses() is used everywhere else. - cf = contentFilter if contentFilter else {} - cf.update(kwargs) - analyses = [] - for ar in self.getAnalysisRequests(): - analyses.extend(ar.getAnalyses(**cf)) - return analyses - - def getSamplers(self): - return getUsers(self, ['Sampler', ]) - - def disposal_date(self): - """Returns the date the retention period ends for this sample based on - the retention period from the Sample Type. If the sample hasn't been - collected yet, returns None - """ - date_sampled = self.getDateSampled() - if not date_sampled: - return None - - # TODO Preservation - preservation's retention period has priority over - # sample type's preservation period - - retention_period = self.getSampleType().getRetentionPeriod() or {} - retention_period_delta = timedelta( - days=int(retention_period.get("days", 0)), - hours=int(retention_period.get("hours", 0)), - minutes=int(retention_period.get("minutes", 0)) - ) - return dt2DT(DT2dt(date_sampled) + retention_period_delta) - - - # TODO This method is only for v1.3.0 migration purposes - # bika_catalog contains an "isValid" index. We will take advantage of this - # index to keep track of the Samples that have been migrated already in - # order to prevent an unnecessary reimport when v1.3.0 is rerun. - def isValid(self): - return self.getMigrated() - - -atapi.registerType(Sample, PROJECTNAME) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 0d989ae38e..8b0fe4846c 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -194,11 +194,6 @@ class IReportFolder(Interface): """Report folder """ -# TODO Remove in >v1.3.0 -class ISample(Interface): - """Sample - """ - class ISampleCondition(Interface): """Sample Condition From b2c6b82f2df0ce9769f269d85713f7a0b5fa77e7 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 20:59:16 +0100 Subject: [PATCH 17/28] Removed Sample profiles as well --- bika/lims/profiles/default/types.xml | 1 - bika/lims/profiles/default/types/Sample.xml | 25 --------------------- 2 files changed, 26 deletions(-) delete mode 100644 bika/lims/profiles/default/types/Sample.xml diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index cae6bde25e..25aa652a08 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -73,7 +73,6 @@ - diff --git a/bika/lims/profiles/default/types/Sample.xml b/bika/lims/profiles/default/types/Sample.xml deleted file mode 100644 index 63a8c2e76c..0000000000 --- a/bika/lims/profiles/default/types/Sample.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - Sample - - ++resource++bika.lims.images/sample.png - Sample - bika.lims - addSample - - - False - True - - - - False - False - base_view - - - From a735457aa3be60eb80085a749d733c74a952c25b Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 20:59:30 +0100 Subject: [PATCH 18/28] Removed stale Sample Partition --- bika/lims/content/samplepartition.py | 91 ------------------- bika/lims/interfaces/__init__.py | 5 - bika/lims/profiles/default/types.xml | 1 - .../default/types/SamplePartition.xml | 23 ----- 4 files changed, 120 deletions(-) delete mode 100644 bika/lims/content/samplepartition.py delete mode 100644 bika/lims/profiles/default/types/SamplePartition.xml diff --git a/bika/lims/content/samplepartition.py b/bika/lims/content/samplepartition.py deleted file mode 100644 index 3df9a60d18..0000000000 --- a/bika/lims/content/samplepartition.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- 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 AccessControl import ClassSecurityInfo -from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin -from Products.Archetypes.public import BaseContent -from Products.Archetypes.public import BooleanField -from Products.Archetypes.public import DateTimeField -from Products.Archetypes.public import ReferenceField -from Products.Archetypes.public import Schema -from Products.Archetypes.public import StringField -from Products.Archetypes.public import registerType -from Products.CMFPlone.utils import safe_unicode -from bika.lims.browser.fields import DurationField -from bika.lims.browser.fields import UIDReferenceField -from bika.lims.config import PROJECTNAME -from bika.lims.content.bikaschema import BikaSchema -from bika.lims.interfaces import ISamplePartition -from zope.interface import implements -from bika.lims.interfaces import IDoNotSupportSnapshots - -schema = BikaSchema.copy() + Schema(( - ReferenceField('Container', - allowed_types=('Container',), - relationship='SamplePartitionContainer', - required=1, - multiValued=0, - ), - ReferenceField('Preservation', - allowed_types=('Preservation',), - relationship='SamplePartitionPreservation', - required=0, - multiValued=0, - ), - BooleanField('Separate', - default=False - ), - UIDReferenceField('Analyses', - allowed_types=('Analysis',), - required=0, - multiValued=1, - ), - DateTimeField('DatePreserved', - ), - StringField('Preserver', - searchable=True - ), - DurationField('RetentionPeriod', - ), -) -) - -schema['title'].required = False - - -class SamplePartition(BaseContent, HistoryAwareMixin): - implements(ISamplePartition, IDoNotSupportSnapshots) - security = ClassSecurityInfo() - displayContentsTab = False - schema = schema - - _at_rename_after_creation = True - - def _renameAfterCreation(self, check_auto_id=False): - from bika.lims.idserver import renameAfterCreation - renameAfterCreation(self) - - def Title(self): - """ Return the Sample ID as title """ - return safe_unicode(self.getId()).encode('utf-8') - - -registerType(SamplePartition, PROJECTNAME) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 8b0fe4846c..5e92ff9de6 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -215,11 +215,6 @@ class ISampleMatrices(Interface): """ -class ISamplePartition(Interface): - """Sample - """ - - class ISamplesFolder(Interface): """Samples Folder """ diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index 25aa652a08..efe7e398e8 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -77,7 +77,6 @@ - diff --git a/bika/lims/profiles/default/types/SamplePartition.xml b/bika/lims/profiles/default/types/SamplePartition.xml deleted file mode 100644 index 166bed5a88..0000000000 --- a/bika/lims/profiles/default/types/SamplePartition.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - Sample Partition - - ++resource++bika.lims.images/samplepartition.png - SamplePartition - bika.lims - addSamplePartition - - - False - True - - False - False - base_view - - - From 8bf637d89041210d0361f20c61cb2daa7ca90327 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 21:17:15 +0100 Subject: [PATCH 19/28] Remove SamplesFolder --- bika/lims/__init__.py | 1 - bika/lims/content/samplesfolder.py | 46 ------------------- bika/lims/interfaces/__init__.py | 5 -- bika/lims/profiles/default/types.xml | 1 - .../profiles/default/types/SamplesFolder.xml | 36 --------------- bika/lims/upgrade/v01_03_003.py | 1 + 6 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 bika/lims/content/samplesfolder.py delete mode 100644 bika/lims/profiles/default/types/SamplesFolder.xml diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index e1f0d547d9..6a2cd8b36d 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -127,7 +127,6 @@ def initialize(context): from content.samplematrix import SampleMatrix # noqa from content.samplepartition import SamplePartition # noqa from content.samplepoint import SamplePoint # noqa - from content.samplesfolder import SamplesFolder # noqa from content.sampletype import SampleType # noqa from content.samplingdeviation import SamplingDeviation # noqa from content.srtemplate import SRTemplate # noqa diff --git a/bika/lims/content/samplesfolder.py b/bika/lims/content/samplesfolder.py deleted file mode 100644 index f134f9e49f..0000000000 --- a/bika/lims/content/samplesfolder.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- 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. - -"""SamplesFolder is a fake folder to live in the nav bar. It has -view from browser/sample.py/SamplesView wired to it. -""" -from Products.ATContentTypes.content import schemata -from Products.Archetypes import atapi -from Products.CMFCore import permissions -from Products.CMFCore.utils import getToolByName -from bika.lims.config import PROJECTNAME -from AccessControl import ClassSecurityInfo -from bika.lims.interfaces import ISamplesFolder, IHaveNoBreadCrumbs -from plone.app.folder import folder -from zope.interface import implements -from bika.lims import bikaMessageFactory as _ -from bika.lims.utils import t - -schema = folder.ATFolderSchema.copy() - -class SamplesFolder(folder.ATFolder): - implements(ISamplesFolder, IHaveNoBreadCrumbs) - displayContentsTab = False - schema = schema - security = ClassSecurityInfo() - -schemata.finalizeATCTSchema(schema, folderish = True, moveDiscussion = False) - -atapi.registerType(SamplesFolder, PROJECTNAME) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 5e92ff9de6..4da9e704db 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -215,11 +215,6 @@ class ISampleMatrices(Interface): """ -class ISamplesFolder(Interface): - """Samples Folder - """ - - class ISamplingDeviation(Interface): """Sampling Deviation """ diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index efe7e398e8..092e92c57d 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -81,7 +81,6 @@ - diff --git a/bika/lims/profiles/default/types/SamplesFolder.xml b/bika/lims/profiles/default/types/SamplesFolder.xml deleted file mode 100644 index 12e2455474..0000000000 --- a/bika/lims/profiles/default/types/SamplesFolder.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - SamplesFolder - - ++resource++bika.lims.images/sample.png - SamplesFolder - bika.lims - addSamplesFolder - - - False - True - - False - False - - - - - - - - - - diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 07b34749a3..d2ba2d0a22 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -43,6 +43,7 @@ TYPES_TO_REMOVE = [ + "SamplesFolder", "BikaCache", # invoices were removed in upgrade step 1.3.0 "InvoiceBatch", From 97a3a6c977cdce64c97ee38af820689423d1993f Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 21:37:58 +0100 Subject: [PATCH 20/28] Removed Sample from factorytool/protperties profiles --- bika/lims/profiles/default/factorytool.xml | 1 - bika/lims/profiles/default/propertiestool.xml | 2 -- 2 files changed, 3 deletions(-) diff --git a/bika/lims/profiles/default/factorytool.xml b/bika/lims/profiles/default/factorytool.xml index 05b631bd10..22967ca7ea 100644 --- a/bika/lims/profiles/default/factorytool.xml +++ b/bika/lims/profiles/default/factorytool.xml @@ -41,7 +41,6 @@ - diff --git a/bika/lims/profiles/default/propertiestool.xml b/bika/lims/profiles/default/propertiestool.xml index 53489e76d5..41412a017b 100644 --- a/bika/lims/profiles/default/propertiestool.xml +++ b/bika/lims/profiles/default/propertiestool.xml @@ -75,8 +75,6 @@ - - From 338f8a93cb9c6c15479e7a80085d41f5999fc1f2 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 21:54:27 +0100 Subject: [PATCH 21/28] Removed ARImport --- CHANGES.rst | 1 + bika/lims/__init__.py | 1 - bika/lims/browser/arimports.py | 221 ---- bika/lims/browser/arimports.zcml | 31 - bika/lims/browser/configure.zcml | 1 - bika/lims/browser/templates/arimport_add.pt | 38 - bika/lims/config.py | 5 - bika/lims/content/arimport.py | 964 ------------------ bika/lims/content/bikasetup.py | 19 - bika/lims/content/client.py | 6 - bika/lims/interfaces/__init__.py | 10 - bika/lims/monkey/contentmenu.py | 1 - bika/lims/permissions.py | 1 - bika/lims/permissions.zcml | 1 - bika/lims/profiles/default/factorytool.xml | 1 - bika/lims/profiles/default/propertiestool.xml | 1 - bika/lims/profiles/default/rolemap.xml | 5 - bika/lims/profiles/default/types.xml | 1 - bika/lims/profiles/default/types/ARImport.xml | 48 - bika/lims/profiles/default/types/Client.xml | 13 - bika/lims/profiles/default/workflows.xml | 7 - .../bika_arimport_workflow/definition.xml | 116 --- .../bika_arimports_workflow/definition.xml | 100 -- bika/lims/setuphandlers.py | 1 - bika/lims/skins/bika/ploneCustom.css.dtml | 89 -- .../lims/tests/files/BikaARImportTemplate.csv | 18 - bika/lims/tests/test_ARImport.py | 249 ----- bika/lims/upgrade/v01_03_003.py | 11 + 28 files changed, 12 insertions(+), 1948 deletions(-) delete mode 100644 bika/lims/browser/arimports.py delete mode 100644 bika/lims/browser/arimports.zcml delete mode 100644 bika/lims/browser/templates/arimport_add.pt delete mode 100644 bika/lims/content/arimport.py delete mode 100644 bika/lims/profiles/default/types/ARImport.xml delete mode 100644 bika/lims/profiles/default/workflows/bika_arimport_workflow/definition.xml delete mode 100644 bika/lims/profiles/default/workflows/bika_arimports_workflow/definition.xml delete mode 100644 bika/lims/tests/files/BikaARImportTemplate.csv delete mode 100644 bika/lims/tests/test_ARImport.py diff --git a/CHANGES.rst b/CHANGES.rst index d238e47e7e..a55cd446d3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -40,6 +40,7 @@ Changelog **Removed** +- #1530 Removed ARImport - #1530 Removed stale type registrations - #1518 Removed stale indexes from `analysis_catalog` - #1516 Removed getResultsRange metadata from analysis_catalog diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index 6a2cd8b36d..3a63bb8ec2 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -73,7 +73,6 @@ def initialize(context): from content.analysisrequestsfolder import AnalysisRequestsFolder # noqa from content.analysisservice import AnalysisService # noqa from content.analysisspec import AnalysisSpec # noqa - from content.arimport import ARImport # noqa from content.arreport import ARReport # noqa from content.artemplate import ARTemplate # noqa from content.attachment import Attachment # noqa diff --git a/bika/lims/browser/arimports.py b/bika/lims/browser/arimports.py deleted file mode 100644 index e576b1db43..0000000000 --- a/bika/lims/browser/arimports.py +++ /dev/null @@ -1,221 +0,0 @@ -# -*- 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. - -import csv -from DateTime.DateTime import DateTime -from bika.lims import bikaMessageFactory as _ -from bika.lims.browser import BrowserView, ulocalized_time -from bika.lims.browser.bika_listing import BikaListingView -from bika.lims.interfaces import IClient -from bika.lims.utils import tmpID -from bika.lims.workflow import getTransitionDate -from plone.app.contentlisting.interfaces import IContentListing -from plone.app.layout.globals.interfaces import IViewView -from plone.protect import CheckAuthenticator -from Products.Archetypes.utils import addStatusMessage -from Products.CMFCore.utils import getToolByName -from Products.CMFCore.WorkflowCore import WorkflowException -from Products.CMFPlone.utils import _createObjectByType -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from zope.interface import alsoProvides -from zope.interface import implements - -import os - - -class ARImportsView(BikaListingView): - implements(IViewView) - - def __init__(self, context, request): - super(ARImportsView, self).__init__(context, request) - request.set('disable_plone.rightcolumn', 1) - alsoProvides(request, IContentListing) - - self.catalog = "portal_catalog" - self.contentFilter = { - 'portal_type': 'ARImport', - 'is_active': True, - 'sort_on': 'sortable_title', - } - self.context_actions = {} - if IClient.providedBy(self.context): - self.context_actions = { - _('AR Import'): { - 'url': 'arimport_add', - 'icon': '++resource++bika.lims.images/add.png'}} - - self.show_select_row = False - self.show_select_column = False - self.pagesize = 50 - self.form_id = "arimports" - - self.icon = \ - self.portal_url + "/++resource++bika.lims.images/arimport_big.png" - self.title = self.context.translate(_("Sample Imports")) - self.description = "" - - self.columns = { - 'Title': {'title': _('Title')}, - 'Client': {'title': _('Client')}, - 'Filename': {'title': _('Filename')}, - 'Creator': {'title': _('Date Created')}, - 'DateCreated': {'title': _('Date Created')}, - 'DateValidated': {'title': _('Date Validated')}, - 'DateImported': {'title': _('Date Imported')}, - 'state_title': {'title': _('State')}, - } - self.review_states = [ - {'id': 'default', - 'title': _('Pending'), - 'contentFilter': {'review_state': ['invalid', 'valid']}, - 'columns': ['Title', - 'Creator', - 'Filename', - 'Client', - 'DateCreated', - 'DateValidated', - 'DateImported', - 'state_title']}, - {'id': 'imported', - 'title': _('Imported'), - 'contentFilter': {'review_state': 'imported'}, - 'columns': ['Title', - 'Creator', - 'Filename', - 'Client', - 'DateCreated', - 'DateValidated', - 'DateImported', - 'state_title']}, - ] - - def folderitems(self, **kwargs): - items = super(ARImportsView, self).folderitems() - for x in range(len(items)): - if 'obj' not in items[x]: - continue - obj = items[x]['obj'] - items[x]['Title'] = obj.title_or_id() - if items[x]['review_state'] == 'invalid': - items[x]['replace']['Title'] = "%s" % ( - obj.absolute_url(), items[x]['Title']) - else: - items[x]['replace']['Title'] = "%s" % ( - obj.absolute_url(), items[x]['Title']) - items[x]['Creator'] = obj.Creator() - items[x]['Filename'] = obj.getFilename() - parent = obj.aq_parent - items[x]['Client'] = parent if IClient.providedBy(parent) else '' - items[x]['replace']['Client'] = "%s" % ( - parent.absolute_url(), parent.Title()) - items[x]['DateCreated'] = ulocalized_time( - obj.created(), long_format=True, time_only=False, context=obj) - date = getTransitionDate(obj, 'validate') - items[x]['DateValidated'] = date if date else '' - date = getTransitionDate(obj, 'import') - items[x]['DateImported'] = date if date else '' - - return items - - -class ClientARImportsView(ARImportsView): - def __init__(self, context, request): - super(ClientARImportsView, self).__init__(context, request) - self.contentFilter['path'] = { - 'query': '/'.join(context.getPhysicalPath()) - } - - self.review_states = [ - {'id': 'default', - 'title': _('Pending'), - 'contentFilter': {'review_state': ['invalid', 'valid']}, - 'columns': ['Title', - 'Creator', - 'Filename', - 'DateCreated', - 'DateValidated', - 'DateImported', - 'state_title']}, - {'id': 'imported', - 'title': _('Imported'), - 'contentFilter': {'review_state': 'imported'}, - 'columns': ['Title', - 'Creator', - 'Filename', - 'DateCreated', - 'DateValidated', - 'DateImported', - 'state_title']}, - ] - - -class ClientARImportAddView(BrowserView): - implements(IViewView) - template = ViewPageTemplateFile('templates/arimport_add.pt') - - def __init__(self, context, request): - super(ClientARImportAddView, self).__init__(context, request) - alsoProvides(request, IContentListing) - - def __call__(self): - request = self.request - form = request.form - CheckAuthenticator(form) - if form.get('submitted'): - # Validate form submission - csvfile = form.get('csvfile') - data = csvfile.read() - lines = data.splitlines() - filename = csvfile.filename - if not csvfile: - addStatusMessage(request, _("No file selected")) - return self.template() - if len(lines) < 3: - addStatusMessage(request, _("Too few lines in CSV file")) - return self.template() - # Create the arimport object - arimport = _createObjectByType("ARImport", self.context, tmpID()) - arimport.processForm() - arimport.setTitle(self.mkTitle(filename)) - arimport.schema['OriginalFile'].set(arimport, data) - # Save all fields from the file into the arimport schema - arimport.save_header_data() - arimport.save_sample_data() - # immediate batch creation if required - arimport.create_or_reference_batch() - # Attempt first validation - try: - workflow = getToolByName(self.context, 'portal_workflow') - workflow.doActionFor(arimport, 'validate') - except WorkflowException: - self.request.response.redirect(arimport.absolute_url() + - "/edit") - else: - return self.template() - - def mkTitle(self, filename): - pc = getToolByName(self.context, 'portal_catalog') - nr = 1 - while True: - newname = '%s-%s' % (os.path.splitext(filename)[0], nr) - existing = pc(portal_type='ARImport', title=newname) - if not existing: - return newname - nr += 1 diff --git a/bika/lims/browser/arimports.zcml b/bika/lims/browser/arimports.zcml deleted file mode 100644 index eb4cf50f12..0000000000 --- a/bika/lims/browser/arimports.zcml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - diff --git a/bika/lims/browser/configure.zcml b/bika/lims/browser/configure.zcml index 44330ff0f5..26a222aac9 100644 --- a/bika/lims/browser/configure.zcml +++ b/bika/lims/browser/configure.zcml @@ -11,7 +11,6 @@ - diff --git a/bika/lims/browser/templates/arimport_add.pt b/bika/lims/browser/templates/arimport_add.pt deleted file mode 100644 index b71d855cc2..0000000000 --- a/bika/lims/browser/templates/arimport_add.pt +++ /dev/null @@ -1,38 +0,0 @@ - - -< i18n:translate="" - tal:content="Add SampleImport"/> - - - - - -
- -

- - Import Sample Data -

- -
- - -
- -
- -
- -
- - - diff --git a/bika/lims/config.py b/bika/lims/config.py index 770da18755..b520cf53f6 100644 --- a/bika/lims/config.py +++ b/bika/lims/config.py @@ -88,11 +88,6 @@ ('r', _('Render in Report')), ('i', _('Ignore in Report')), )) -ARIMPORT_OPTIONS = DisplayList(( - ('c', _('Classic')), - ('p', _('Profiles')), - # ('s', _('Special')), -)) GENDERS = DisplayList(( ('male', _('Male')), ('female', _('Female')), diff --git a/bika/lims/content/arimport.py b/bika/lims/content/arimport.py deleted file mode 100644 index fc2f245156..0000000000 --- a/bika/lims/content/arimport.py +++ /dev/null @@ -1,964 +0,0 @@ -# -*- 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 AccessControl import ClassSecurityInfo -import csv -from copy import deepcopy -from DateTime.DateTime import DateTime -from Products.Archetypes.event import ObjectInitializedEvent -from Products.CMFCore.WorkflowCore import WorkflowException -from bika.lims import bikaMessageFactory as _ -from bika.lims.browser import ulocalized_time -from bika.lims.config import PROJECTNAME -from bika.lims.content.bikaschema import BikaSchema -from bika.lims.content.analysisrequest import schema as ar_schema -from bika.lims.content.sample import schema as sample_schema -from bika.lims.idserver import renameAfterCreation -from bika.lims.interfaces import IARImport, IClient -from bika.lims.utils import tmpID -from bika.lims.utils.analysisrequest import create_analysisrequest -from bika.lims.vocabularies import CatalogVocabulary -from bika.lims.workflow import doActionFor -from collective.progressbar.events import InitialiseProgressBar -from collective.progressbar.events import ProgressBar -from collective.progressbar.events import ProgressState -from collective.progressbar.events import UpdateProgressEvent -from Products.Archetypes import atapi -from Products.Archetypes.public import * -from plone.app.blob.field import FileField as BlobFileField -from Products.Archetypes.references import HoldingReference -from Products.Archetypes.utils import addStatusMessage -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import _createObjectByType -from Products.DataGridField import CheckboxColumn -from Products.DataGridField import Column -from Products.DataGridField import DataGridField -from Products.DataGridField import DataGridWidget -from Products.DataGridField import DateColumn -from Products.DataGridField import LinesColumn -from Products.DataGridField import SelectColumn -from zope import event -from zope.event import notify -from zope.i18nmessageid import MessageFactory -from zope.interface import implements - -from bika.lims.browser.widgets import ReferenceWidget as bReferenceWidget - -import sys -import transaction - -_p = MessageFactory(u"plone") - -OriginalFile = BlobFileField( - 'OriginalFile', - widget=ComputedWidget( - visible=False - ), -) - -Filename = StringField( - 'Filename', - widget=StringWidget( - label=_('Original Filename'), - visible=True - ), -) - -NrSamples = StringField( - 'NrSamples', - widget=StringWidget( - label=_('Number of samples'), - visible=True - ), -) - -ClientName = StringField( - 'ClientName', - searchable=True, - widget=StringWidget( - label=_("Client Name"), - ), -) - -ClientID = StringField( - 'ClientID', - searchable=True, - widget=StringWidget( - label=_('Client ID'), - ), -) - -ClientOrderNumber = StringField( - 'ClientOrderNumber', - searchable=True, - widget=StringWidget( - label=_('Client Order Number'), - ), -) - -ClientReference = StringField( - 'ClientReference', - searchable=True, - widget=StringWidget( - label=_('Client Reference'), - ), -) - -Contact = ReferenceField( - 'Contact', - allowed_types=('Contact',), - relationship='ARImportContact', - default_method='getContactUIDForUser', - referenceClass=HoldingReference, - vocabulary_display_path_bound=sys.maxint, - widget=ReferenceWidget( - label=_('Primary Contact'), - size=20, - visible=True, - base_query={'is_active': True}, - showOn=True, - popup_width='300px', - colModel=[{'columnName': 'UID', 'hidden': True}, - {'columnName': 'Fullname', 'width': '100', - 'label': _('Name')}], - ), -) - -Batch = ReferenceField( - 'Batch', - allowed_types=('Batch',), - relationship='ARImportBatch', - widget=bReferenceWidget( - label=_('Batch'), - visible=True, - catalog_name='bika_catalog', - base_query={'review_state': 'open'}, - showOn=True, - ), -) - -CCContacts = DataGridField( - 'CCContacts', - allow_insert=False, - allow_delete=False, - allow_reorder=False, - allow_empty_rows=False, - columns=('CCNamesReport', - 'CCEmailsReport', - 'CCNamesInvoice', - 'CCEmailsInvoice'), - default=[{'CCNamesReport': [], - 'CCEmailsReport': [], - 'CCNamesInvoice': [], - 'CCEmailsInvoice': [] - }], - widget=DataGridWidget( - columns={ - 'CCNamesReport': LinesColumn('Report CC Contacts'), - 'CCEmailsReport': LinesColumn('Report CC Emails'), - 'CCNamesInvoice': LinesColumn('Invoice CC Contacts'), - 'CCEmailsInvoice': LinesColumn('Invoice CC Emails') - } - ) -) - -SampleData = DataGridField( - 'SampleData', - allow_insert=True, - allow_delete=True, - allow_reorder=False, - allow_empty_rows=False, - allow_oddeven=True, - columns=('ClientSampleID', - 'SamplingDate', - 'DateSampled', - 'SamplePoint', - 'SampleMatrix', - 'SampleType', # not a schema field! - 'ContainerType', # not a schema field! - 'Analyses', # not a schema field! - 'Profiles' # not a schema field! - ), - widget=DataGridWidget( - label=_('Samples'), - columns={ - 'ClientSampleID': Column('Sample ID'), - 'SamplingDate': DateColumn('Sampling Date'), - 'DateSampled': DateColumn('Date Sampled'), - 'SamplePoint': SelectColumn( - 'Sample Point', vocabulary='Vocabulary_SamplePoint'), - 'SampleMatrix': SelectColumn( - 'Sample Matrix', vocabulary='Vocabulary_SampleMatrix'), - 'SampleType': SelectColumn( - 'Sample Type', vocabulary='Vocabulary_SampleType'), - 'ContainerType': SelectColumn( - 'Container', vocabulary='Vocabulary_ContainerType'), - 'Analyses': LinesColumn('Analyses'), - 'Profiles': LinesColumn('Profiles'), - } - ) -) - -Errors = LinesField( - 'Errors', - widget=LinesWidget( - label=_('Errors'), - rows=10, - ) -) - -schema = BikaSchema.copy() + Schema(( - OriginalFile, - Filename, - NrSamples, - ClientName, - ClientID, - ClientOrderNumber, - ClientReference, - Contact, - CCContacts, - Batch, - SampleData, - Errors, -)) - -schema['title'].validators = () -# Update the validation layer after change the validator in runtime -schema['title']._validationLayer() - - -class ARImport(BaseFolder): - security = ClassSecurityInfo() - schema = schema - displayContentsTab = False - implements(IARImport) - - _at_rename_after_creation = True - - def _renameAfterCreation(self, check_auto_id=False): - renameAfterCreation(self) - - def guard_validate_transition(self): - """We may only attempt validation if file data has been uploaded. - """ - data = self.getOriginalFile() - if data and len(data): - return True - - # TODO Workflow - ARImport - Remove - def workflow_before_validate(self): - """This function transposes values from the provided file into the - ARImport object's fields, and checks for invalid values. - - If errors are found: - - Validation transition is aborted. - - Errors are stored on object and displayed to user. - - """ - # Re-set the errors on this ARImport each time validation is attempted. - # When errors are detected they are immediately appended to this field. - self.setErrors([]) - - self.validate_headers() - self.validate_samples() - - if self.getErrors(): - addStatusMessage(self.REQUEST, _p('Validation errors.'), 'error') - transaction.commit() - self.REQUEST.response.write( - '' % ( - self.absolute_url())) - self.REQUEST.response.write( - '' % ( - self.absolute_url())) - - def at_post_edit_script(self): - workflow = getToolByName(self, 'portal_workflow') - trans_ids = [t['id'] for t in workflow.getTransitionsFor(self)] - if 'validate' in trans_ids: - workflow.doActionFor(self, 'validate') - - def workflow_script_import(self): - """Create objects from valid ARImport - """ - bsc = getToolByName(self, 'bika_setup_catalog') - client = self.aq_parent - - title = _('Submitting Sample Import') - description = _('Creating and initialising objects') - bar = ProgressBar(self, self.REQUEST, title, description) - notify(InitialiseProgressBar(bar)) - - profiles = [x.getObject() for x in bsc(portal_type='AnalysisProfile')] - - gridrows = self.schema['SampleData'].get(self) - row_cnt = 0 - for therow in gridrows: - row = deepcopy(therow) - row_cnt += 1 - - # Profiles are titles, profile keys, or UIDS: convert them to UIDs. - newprofiles = [] - for title in row['Profiles']: - objects = [x for x in profiles - if title in (x.getProfileKey(), x.UID(), x.Title())] - for obj in objects: - newprofiles.append(obj.UID()) - row['Profiles'] = newprofiles - - # Same for analyses - newanalyses = set(self.get_row_services(row) + - self.get_row_profile_services(row)) - # get batch - batch = self.schema['Batch'].get(self) - if batch: - row['Batch'] = batch.UID() - # Add AR fields from schema into this row's data - row['ClientReference'] = self.getClientReference() - row['ClientOrderNumber'] = self.getClientOrderNumber() - contact_uid =\ - self.getContact().UID() if self.getContact() else None - row['Contact'] = contact_uid - # Creating analysis request from gathered data - ar = create_analysisrequest( - client, - self.REQUEST, - row, - analyses=list(newanalyses),) - - # progress marker update - progress_index = float(row_cnt) / len(gridrows) * 100 - progress = ProgressState(self.REQUEST, progress_index) - notify(UpdateProgressEvent(progress)) - - # document has been written to, and redirect() fails here - self.REQUEST.response.write( - '' % ( - self.absolute_url())) - - def get_header_values(self): - """Scrape the "Header" values from the original input file - """ - lines = self.getOriginalFile().data.splitlines() - reader = csv.reader(lines) - header_fields = header_data = [] - for row in reader: - if not any(row): - continue - if row[0].strip().lower() == 'header': - header_fields = [x.strip() for x in row][1:] - continue - if row[0].strip().lower() == 'header data': - header_data = [x.strip() for x in row][1:] - break - if not (header_data or header_fields): - return None - if not (header_data and header_fields): - self.error("File is missing header row or header data") - return None - # inject us out of here - values = dict(zip(header_fields, header_data)) - # blank cell from sheet will probably make it in here: - if '' in values: - del (values['']) - return values - - def save_header_data(self): - """Save values from the file's header row into their schema fields. - """ - client = self.aq_parent - - headers = self.get_header_values() - if not headers: - return False - - # Plain header fields that can be set into plain schema fields: - for h, f in [ - ('File name', 'Filename'), - ('No of Samples', 'NrSamples'), - ('Client name', 'ClientName'), - ('Client ID', 'ClientID'), - ('Client Order Number', 'ClientOrderNumber'), - ('Client Reference', 'ClientReference') - ]: - v = headers.get(h, None) - if v: - field = self.schema[f] - field.set(self, v) - del (headers[h]) - - # Primary Contact - v = headers.get('Contact', None) - contacts = [x for x in client.objectValues('Contact')] - contact = [c for c in contacts if c.Title() == v] - if contact: - self.schema['Contact'].set(self, contact) - else: - self.error("Specified contact '%s' does not exist; using '%s'"% - (v, contacts[0].Title())) - self.schema['Contact'].set(self, contacts[0]) - del (headers['Contact']) - - # CCContacts - field_value = { - 'CCNamesReport': '', - 'CCEmailsReport': '', - 'CCNamesInvoice': '', - 'CCEmailsInvoice': '' - } - for h, f in [ - # csv header name DataGrid Column ID - ('CC Names - Report', 'CCNamesReport'), - ('CC Emails - Report', 'CCEmailsReport'), - ('CC Names - Invoice', 'CCNamesInvoice'), - ('CC Emails - Invoice', 'CCEmailsInvoice'), - ]: - if h in headers: - values = [x.strip() for x in headers.get(h, '').split(",")] - field_value[f] = values if values else '' - del (headers[h]) - self.schema['CCContacts'].set(self, [field_value]) - - if headers: - unexpected = ','.join(headers.keys()) - self.error("Unexpected header fields: %s" % unexpected) - - def get_sample_values(self): - """Read the rows specifying Samples and return a dictionary with - related data. - - keys are: - headers - row with "Samples" in column 0. These headers are - used as dictionary keys in the rows below. - prices - Row with "Analysis Price" in column 0. - total_analyses - Row with "Total analyses" in colmn 0 - price_totals - Row with "Total price excl Tax" in column 0 - samples - All other sample rows. - - """ - res = {'samples': []} - lines = self.getOriginalFile().data.splitlines() - reader = csv.reader(lines) - next_rows_are_sample_rows = False - for row in reader: - if not any(row): - continue - if next_rows_are_sample_rows: - vals = [x.strip() for x in row] - if not any(vals): - continue - res['samples'].append(zip(res['headers'], vals)) - elif row[0].strip().lower() == 'samples': - res['headers'] = [x.strip() for x in row] - elif row[0].strip().lower() == 'analysis price': - res['prices'] = \ - zip(res['headers'], [x.strip() for x in row]) - elif row[0].strip().lower() == 'total analyses': - res['total_analyses'] = \ - zip(res['headers'], [x.strip() for x in row]) - elif row[0].strip().lower() == 'total price excl tax': - res['price_totals'] = \ - zip(res['headers'], [x.strip() for x in row]) - next_rows_are_sample_rows = True - return res - - def save_sample_data(self): - """Save values from the file's header row into the DataGrid columns - after doing some very basic validation - """ - bsc = getToolByName(self, 'bika_setup_catalog') - keywords = self.bika_setup_catalog.uniqueValuesFor('getKeyword') - profiles = [] - for p in bsc(portal_type='AnalysisProfile'): - p = p.getObject() - profiles.append(p.Title()) - profiles.append(p.getProfileKey()) - - sample_data = self.get_sample_values() - if not sample_data: - return False - - # columns that we expect, but do not find, are listed here. - # we report on them only once, after looping through sample rows. - missing = set() - - # This contains all sample header rows that were not handled - # by this code - unexpected = set() - - # Save other errors here instead of sticking them directly into - # the field, so that they show up after MISSING and before EXPECTED - errors = [] - - # This will be the new sample-data field value, when we are done. - grid_rows = [] - - row_nr = 0 - for row in sample_data['samples']: - row = dict(row) - row_nr += 1 - - # sid is just for referring the user back to row X in their - # in put spreadsheet - gridrow = {'sid': row['Samples']} - del (row['Samples']) - - # We'll use this later to verify the number against selections - if 'Total number of Analyses or Profiles' in row: - nr_an = row['Total number of Analyses or Profiles'] - del (row['Total number of Analyses or Profiles']) - else: - nr_an = 0 - try: - nr_an = int(nr_an) - except ValueError: - nr_an = 0 - - # TODO this is ignored and is probably meant to serve some purpose. - del (row['Price excl Tax']) - - # ContainerType - not part of sample or AR schema - if 'ContainerType' in row: - title = row['ContainerType'] - if title: - obj = self.lookup(('ContainerType',), - Title=row['ContainerType']) - if obj: - gridrow['ContainerType'] = obj[0].UID - del (row['ContainerType']) - - if 'SampleMatrix' in row: - # SampleMatrix - not part of sample or AR schema - title = row['SampleMatrix'] - if title: - obj = self.lookup(('SampleMatrix',), - Title=row['SampleMatrix']) - if obj: - gridrow['SampleMatrix'] = obj[0].UID - del (row['SampleMatrix']) - - # match against sample schema - for k, v in row.items(): - if k in ['Analyses', 'Profiles']: - continue - if k in sample_schema: - del (row[k]) - if v: - try: - value = self.munge_field_value( - sample_schema, row_nr, k, v) - gridrow[k] = value - except ValueError as e: - errors.append(e.message) - - # match against ar schema - for k, v in row.items(): - if k in ['Analyses', 'Profiles']: - continue - if k in ar_schema: - del (row[k]) - if v: - try: - value = self.munge_field_value( - ar_schema, row_nr, k, v) - gridrow[k] = value - except ValueError as e: - errors.append(e.message) - - # Count and remove Keywords and Profiles from the list - gridrow['Analyses'] = [] - for k, v in row.items(): - if k in keywords: - del (row[k]) - if str(v).strip().lower() not in ('', '0', 'false'): - gridrow['Analyses'].append(k) - gridrow['Profiles'] = [] - for k, v in row.items(): - if k in profiles: - del (row[k]) - if str(v).strip().lower() not in ('', '0', 'false'): - gridrow['Profiles'].append(k) - if len(gridrow['Analyses']) + len(gridrow['Profiles']) != nr_an: - errors.append( - "Row %s: Number of analyses does not match provided value" % - row_nr) - - grid_rows.append(gridrow) - - self.setSampleData(grid_rows) - - if missing: - self.error("SAMPLES: Missing expected fields: %s" % - ','.join(missing)) - - for thing in errors: - self.error(thing) - - if unexpected: - self.error("Unexpected header fields: %s" % - ','.join(unexpected)) - - def get_batch_header_values(self): - """Scrape the "Batch Header" values from the original input file - """ - lines = self.getOriginalFile().data.splitlines() - reader = csv.reader(lines) - batch_headers = batch_data = [] - for row in reader: - if not any(row): - continue - if row[0].strip().lower() == 'batch header': - batch_headers = [x.strip() for x in row][1:] - continue - if row[0].strip().lower() == 'batch data': - batch_data = [x.strip() for x in row][1:] - break - if not (batch_data or batch_headers): - return None - if not (batch_data and batch_headers): - self.error("Missing batch headers or data") - return None - # Inject us out of here - values = dict(zip(batch_headers, batch_data)) - return values - - def create_or_reference_batch(self): - """Save reference to batch, if existing batch specified - Create new batch, if possible with specified values - """ - client = self.aq_parent - batch_headers = self.get_batch_header_values() - if not batch_headers: - return False - # if the Batch's Title is specified and exists, no further - # action is required. We will just set the Batch field to - # use the existing object. - batch_title = batch_headers.get('title', False) - if batch_title: - existing_batch = [x for x in client.objectValues('Batch') - if x.title == batch_title] - if existing_batch: - self.setBatch(existing_batch[0]) - return existing_batch[0] - # If the batch title is specified but does not exist, - # we will attempt to create the bach now. - if 'title' in batch_headers: - if 'id' in batch_headers: - del (batch_headers['id']) - if '' in batch_headers: - del (batch_headers['']) - batch = _createObjectByType('Batch', client, tmpID()) - batch.processForm() - batch.edit(**batch_headers) - self.setBatch(batch) - - def munge_field_value(self, schema, row_nr, fieldname, value): - """Convert a spreadsheet value into a field value that fits in - the corresponding schema field. - - boolean: All values are true except '', 'false', or '0'. - - reference: The title of an object in field.allowed_types; - returns a UID or list of UIDs - - datetime: returns a string value from ulocalized_time - - Tho this is only used during "Saving" of csv data into schema fields, - it will flag 'validation' errors, as this is the only chance we will - get to complain about these field values. - - """ - field = schema[fieldname] - if field.type == 'boolean': - value = str(value).strip().lower() - value = '' if value in ['0', 'no', 'false', 'none'] else '1' - return value - if field.type == 'reference': - value = str(value).strip() - brains = self.lookup(field.allowed_types, Title=value) - if not brains: - brains = self.lookup(field.allowed_types, UID=value) - if not brains: - raise ValueError('Row %s: value is invalid (%s=%s)' % ( - row_nr, fieldname, value)) - if field.multiValued: - return [b.UID for b in brains] if brains else [] - else: - return brains[0].UID if brains else None - if field.type == 'datetime': - try: - value = DateTime(value) - return ulocalized_time( - value, long_format=True, time_only=False, context=self) - except: - raise ValueError('Row %s: value is invalid (%s=%s)' % ( - row_nr, fieldname, value)) - return str(value) - - def validate_headers(self): - """Validate headers fields from schema - """ - - pc = getToolByName(self, 'portal_catalog') - pu = getToolByName(self, "plone_utils") - - client = self.aq_parent - - # Verify Client Name - if self.getClientName() != client.Title(): - self.error("%s: value is invalid (%s)." % ( - 'Client name', self.getClientName())) - - # Verify Client ID - if self.getClientID() != client.getClientID(): - self.error("%s: value is invalid (%s)." % ( - 'Client ID', self.getClientID())) - - existing_arimports = pc(portal_type='ARImport', - review_state=['valid', 'imported']) - # Verify Client Order Number - for arimport in existing_arimports: - if arimport.UID == self.UID() \ - or not arimport.getClientOrderNumber(): - continue - arimport = arimport.getObject() - - if arimport.getClientOrderNumber() == self.getClientOrderNumber(): - self.error('%s: already used by existing ARImport.' % - 'ClientOrderNumber') - break - - # Verify Client Reference - for arimport in existing_arimports: - if arimport.UID == self.UID() \ - or not arimport.getClientReference(): - continue - arimport = arimport.getObject() - if arimport.getClientReference() == self.getClientReference(): - self.error('%s: already used by existing ARImport.' % - 'ClientReference') - break - - # getCCContacts has no value if object is not complete (eg during test) - if self.getCCContacts(): - cc_contacts = self.getCCContacts()[0] - contacts = [x for x in client.objectValues('Contact')] - contact_names = [c.Title() for c in contacts] - # validate Contact existence in this Client - for k in ['CCNamesReport', 'CCNamesInvoice']: - for val in cc_contacts[k]: - if val and val not in contact_names: - self.error('%s: value is invalid (%s)' % (k, val)) - else: - cc_contacts = {'CCNamesReport': [], - 'CCEmailsReport': [], - 'CCNamesInvoice': [], - 'CCEmailsInvoice': [] - } - # validate Contact existence in this Client - for k in ['CCEmailsReport', 'CCEmailsInvoice']: - for val in cc_contacts.get(k, []): - if val and not pu.validateSingleNormalizedEmailAddress(val): - self.error('%s: value is invalid (%s)' % (k, val)) - - def validate_samples(self): - """Scan through the SampleData values and make sure - that each one is correct - """ - - bsc = getToolByName(self, 'bika_setup_catalog') - keywords = bsc.uniqueValuesFor('getKeyword') - profiles = [] - for p in bsc(portal_type='AnalysisProfile'): - p = p.getObject() - profiles.append(p.Title()) - profiles.append(p.getProfileKey()) - - row_nr = 0 - for gridrow in self.getSampleData(): - row_nr += 1 - - # validate against sample and ar schemas - for k, v in gridrow.items(): - if k in ['Analysis', 'Profiles']: - break - if k in sample_schema: - try: - self.validate_against_schema( - sample_schema, row_nr, k, v) - continue - except ValueError as e: - self.error(e.message) - break - if k in ar_schema: - try: - self.validate_against_schema( - ar_schema, row_nr, k, v) - except ValueError as e: - self.error(e.message) - - an_cnt = 0 - for v in gridrow['Analyses']: - if v and v not in keywords: - self.error("Row %s: value is invalid (%s=%s)" % - ('Analysis keyword', row_nr, v)) - else: - an_cnt += 1 - for v in gridrow['Profiles']: - if v and v not in profiles: - self.error("Row %s: value is invalid (%s=%s)" % - ('Profile Title', row_nr, v)) - else: - an_cnt += 1 - if not an_cnt: - self.error("Row %s: No valid analyses or profiles" % row_nr) - - def validate_against_schema(self, schema, row_nr, fieldname, value): - """ - """ - field = schema[fieldname] - if field.type == 'boolean': - value = str(value).strip().lower() - return value - if field.type == 'reference': - value = str(value).strip() - if field.required and not value: - raise ValueError("Row %s: %s field requires a value" % ( - row_nr, fieldname)) - if not value: - return value - brains = self.lookup(field.allowed_types, UID=value) - if not brains: - raise ValueError("Row %s: value is invalid (%s=%s)" % ( - row_nr, fieldname, value)) - if field.multiValued: - return [b.UID for b in brains] if brains else [] - else: - return brains[0].UID if brains else None - if field.type == 'datetime': - try: - ulocalized_time(DateTime(value), long_format=True, - time_only=False, context=self) - except: - raise ValueError('Row %s: value is invalid (%s=%s)' % ( - row_nr, fieldname, value)) - return value - - def lookup(self, allowed_types, **kwargs): - """Lookup an object of type (allowed_types). kwargs is sent - directly to the catalog. - """ - at = getToolByName(self, 'archetype_tool') - for portal_type in allowed_types: - catalog = at.catalog_map.get(portal_type, [None])[0] - catalog = getToolByName(self, catalog) - kwargs['portal_type'] = portal_type - brains = catalog(**kwargs) - if brains: - return brains - - def get_row_services(self, row): - """Return a list of services which are referenced in Analyses. - values may be UID, Title or Keyword. - """ - bsc = getToolByName(self, 'bika_setup_catalog') - services = set() - for val in row.get('Analyses', []): - brains = bsc(portal_type='AnalysisService', getKeyword=val) - if not brains: - brains = bsc(portal_type='AnalysisService', title=val) - if not brains: - brains = bsc(portal_type='AnalysisService', UID=val) - if brains: - services.add(brains[0].UID) - else: - self.error("Invalid analysis specified: %s" % val) - return list(services) - - def get_row_profile_services(self, row): - """Return a list of services which are referenced in profiles - values may be UID, Title or ProfileKey. - """ - bsc = getToolByName(self, 'bika_setup_catalog') - services = set() - profiles = [x.getObject() for x in bsc(portal_type='AnalysisProfile')] - for val in row.get('Profiles', []): - objects = [x for x in profiles - if val in (x.getProfileKey(), x.UID(), x.Title())] - if objects: - for service in objects[0].getService(): - services.add(service.UID()) - else: - self.error("Invalid profile specified: %s" % val) - return list(services) - - def get_row_container(self, row): - """Return a sample container - """ - bsc = getToolByName(self, 'bika_setup_catalog') - val = row.get('Container', False) - if val: - brains = bsc(portal_type='Container', UID=row['Container']) - if brains: - brains[0].getObject() - brains = bsc(portal_type='ContainerType', UID=row['Container']) - if brains: - # XXX Cheating. The calculation of capacity vs. volume is not done. - return brains[0].getObject() - return None - - def get_row_profiles(self, row): - bsc = getToolByName(self, 'bika_setup_catalog') - profiles = [] - for profile_title in row.get('Profiles', []): - profile = bsc(portal_type='AnalysisProfile', title=profile_title) - profiles.append(profile) - return profiles - - def Vocabulary_SamplePoint(self): - vocabulary = CatalogVocabulary(self) - vocabulary.catalog = 'bika_setup_catalog' - folders = [self.bika_setup.bika_samplepoints] - if IClient.providedBy(self.aq_parent): - folders.append(self.aq_parent) - return vocabulary(allow_blank=True, portal_type='SamplePoint') - - def Vocabulary_SampleMatrix(self): - vocabulary = CatalogVocabulary(self) - vocabulary.catalog = 'bika_setup_catalog' - return vocabulary(allow_blank=True, portal_type='SampleMatrix') - - def Vocabulary_SampleType(self): - vocabulary = CatalogVocabulary(self) - vocabulary.catalog = 'bika_setup_catalog' - folders = [self.bika_setup.bika_sampletypes] - if IClient.providedBy(self.aq_parent): - folders.append(self.aq_parent) - return vocabulary(allow_blank=True, portal_type='SampleType') - - def Vocabulary_ContainerType(self): - vocabulary = CatalogVocabulary(self) - vocabulary.catalog = 'bika_setup_catalog' - return vocabulary(allow_blank=True, portal_type='ContainerType') - - def error(self, msg): - errors = list(self.getErrors()) - errors.append(msg) - self.setErrors(errors) - - -atapi.registerType(ARImport, PROJECTNAME) diff --git a/bika/lims/content/bikasetup.py b/bika/lims/content/bikasetup.py index d5464f86d8..46ea7ca27b 100644 --- a/bika/lims/content/bikasetup.py +++ b/bika/lims/content/bikasetup.py @@ -46,7 +46,6 @@ from bika.lims.browser.widgets import DurationWidget from bika.lims.browser.widgets import RecordsWidget from bika.lims.browser.widgets import RejectionSetupWidget -from bika.lims.config import ARIMPORT_OPTIONS from bika.lims.config import ATTACHMENT_OPTIONS from bika.lims.config import CURRENCIES from bika.lims.config import WEEKDAYS @@ -388,19 +387,6 @@ def getCounterTypes(self, instance=None): format='select', ) ), - LinesField( - 'ARImportOption', - schemata="Analyses", - vocabulary=ARIMPORT_OPTIONS, - widget=MultiSelectionWidget( - visible=False, - label=_("AR Import options"), - description=_( - "'Classic' indicates importing samples per sample and " - "analysis service selection. With 'Profiles', analysis profile keywords " - "are used to select multiple analysis services together"), - ) - ), StringField( 'ARAttachmentOption', schemata="Analyses", @@ -717,11 +703,6 @@ def getCounterTypes(self, instance=None): schemata="ID Server", default=[ { - 'form': 'AI-{seq:03d}', - 'portal_type': 'ARImport', - 'sequence_type': 'generated', - 'split_length': 1 - }, { 'form': 'B-{seq:03d}', 'portal_type': 'Batch', 'prefix': 'batch', diff --git a/bika/lims/content/client.py b/bika/lims/content/client.py index f6dc8f5853..030b3e46c4 100644 --- a/bika/lims/content/client.py +++ b/bika/lims/content/client.py @@ -39,7 +39,6 @@ from bika.lims.browser.fields import EmailsField from bika.lims.browser.widgets import ReferenceWidget from bika.lims.catalog.bikasetup_catalog import SETUP_CATALOG -from bika.lims.config import ARIMPORT_OPTIONS from bika.lims.config import DECIMAL_MARKS from bika.lims.config import PROJECTNAME from bika.lims.content.attachment import Attachment @@ -197,11 +196,6 @@ def getContactUIDForUser(self): if len(r) == 1: return r[0].UID - security.declarePublic("getARImportOptions") - - def getARImportOptions(self): - return ARIMPORT_OPTIONS - def getContacts(self, only_active=True): """Return an array containing the contacts from this Client """ diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 4da9e704db..fde2a5415c 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -720,16 +720,6 @@ class ISetupDataImporter(Interface): """ -class IARImportFolder(Interface): - """Marker interface for a folder that contains ARImports - """ - - -class IARImport(Interface): - """Marker interface for an ARImport - """ - - class IPricelist(Interface): """Folder view marker for Pricelist """ diff --git a/bika/lims/monkey/contentmenu.py b/bika/lims/monkey/contentmenu.py index fadfc532c2..f830799c90 100644 --- a/bika/lims/monkey/contentmenu.py +++ b/bika/lims/monkey/contentmenu.py @@ -23,7 +23,6 @@ def contentmenu_factories_available(self): """ if hasattr(self._addContext(), 'portal_type') \ and self._addContext().portal_type in [ - 'ARImport', 'Batch', 'Client', 'AnalysisRequest', diff --git a/bika/lims/permissions.py b/bika/lims/permissions.py index 9300c65cfd..73338f2439 100644 --- a/bika/lims/permissions.py +++ b/bika/lims/permissions.py @@ -181,7 +181,6 @@ EditWorksheet = "senaite.core: Edit Worksheet" ManageBika = "senaite.core: Manage Bika" ManageAnalysisRequests = "senaite.core: Manage Analysis Requests" -ManageARImport = "senaite.core: Manage ARImport" ManageInvoices = "senaite.core: Manage Invoices" ManageLoginDetails = "senaite.core: Manage Login Details" ManageReference = "senaite.core: Manage Reference" diff --git a/bika/lims/permissions.zcml b/bika/lims/permissions.zcml index fd54bde24f..d74e03db6b 100644 --- a/bika/lims/permissions.zcml +++ b/bika/lims/permissions.zcml @@ -152,7 +152,6 @@ - diff --git a/bika/lims/profiles/default/factorytool.xml b/bika/lims/profiles/default/factorytool.xml index 22967ca7ea..83545eae20 100644 --- a/bika/lims/profiles/default/factorytool.xml +++ b/bika/lims/profiles/default/factorytool.xml @@ -1,7 +1,6 @@ - diff --git a/bika/lims/profiles/default/propertiestool.xml b/bika/lims/profiles/default/propertiestool.xml index 41412a017b..ac55c65ebe 100644 --- a/bika/lims/profiles/default/propertiestool.xml +++ b/bika/lims/profiles/default/propertiestool.xml @@ -37,7 +37,6 @@ - diff --git a/bika/lims/profiles/default/rolemap.xml b/bika/lims/profiles/default/rolemap.xml index f9eaef33e7..76c86aa8e9 100644 --- a/bika/lims/profiles/default/rolemap.xml +++ b/bika/lims/profiles/default/rolemap.xml @@ -831,11 +831,6 @@ - - - - - diff --git a/bika/lims/profiles/default/types.xml b/bika/lims/profiles/default/types.xml index 092e92c57d..bb8b017c30 100644 --- a/bika/lims/profiles/default/types.xml +++ b/bika/lims/profiles/default/types.xml @@ -2,7 +2,6 @@ Controls the available content types in your portal - diff --git a/bika/lims/profiles/default/types/ARImport.xml b/bika/lims/profiles/default/types/ARImport.xml deleted file mode 100644 index facf8b8055..0000000000 --- a/bika/lims/profiles/default/types/ARImport.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - ARImport - - ++resource++bika.lims.images/arimport.png - ARImport - bika.lims - addARImport - False - True - - - False - arimport_view - - - - - - - - - - - - - - diff --git a/bika/lims/profiles/default/types/Client.xml b/bika/lims/profiles/default/types/Client.xml index ec6dca0c6b..4507ff3640 100644 --- a/bika/lims/profiles/default/types/Client.xml +++ b/bika/lims/profiles/default/types/Client.xml @@ -16,7 +16,6 @@ False True - @@ -85,18 +84,6 @@ - - - - Contains workflow definitions for your portal - @@ -435,12 +434,6 @@ - - - - - - diff --git a/bika/lims/profiles/default/workflows/bika_arimport_workflow/definition.xml b/bika/lims/profiles/default/workflows/bika_arimport_workflow/definition.xml deleted file mode 100644 index 731ae02331..0000000000 --- a/bika/lims/profiles/default/workflows/bika_arimport_workflow/definition.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - Modify portal content - senaite.core: Manage ARImport - - - - - LabClerk - LabManager - Manager - - - LabClerk - LabManager - Manager - - - - - - - LabClerk - LabManager - Manager - - - LabClerk - LabManager - Manager - - - - - - - LabClerk - LabManager - Manager - - - LabClerk - LabManager - Manager - - - - - Import - - senaite.core: Manage ARImport - - - - - Validate - - senaite.core: Manage ARImport - python:here.guard_validate_transition() - - - - - Previous transition - - transition/getId|nothing - - - - - - - The ID of the user who performed the last transition - - user/getId - - - - - - - Comment about the last transition - - python:state_change.kwargs.get('comment', '') - - - - - - - Provides access to workflow history - - state_change/getHistory - - - - - - - When the previous transition was performed - - state_change/getDateTime - - - - - - diff --git a/bika/lims/profiles/default/workflows/bika_arimports_workflow/definition.xml b/bika/lims/profiles/default/workflows/bika_arimports_workflow/definition.xml deleted file mode 100644 index 98b9bd1304..0000000000 --- a/bika/lims/profiles/default/workflows/bika_arimports_workflow/definition.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - senaite.core: Manage ARImport - List folder contents - Add portal content - Delete objects - View - - - - - - - - - LabClerk - LabManager - Manager - - - - - Member - Manager - LabManager - - - - - Manager - LabManager - - - - - Manager - - - - - Member - Manager - - - - - - Previous transition - - transition/getId|nothing - - - - - - - The ID of the user who performed the last transition - - user/getId - - - - - - - Comment about the last transition - - python:state_change.kwargs.get('comment', '') - - - - - - - Provides access to workflow history - - state_change/getHistory - - - - - - - When the previous transition was performed - - state_change/getDateTime - - - - - - diff --git a/bika/lims/setuphandlers.py b/bika/lims/setuphandlers.py index c84f37c0ed..e3c9d65861 100644 --- a/bika/lims/setuphandlers.py +++ b/bika/lims/setuphandlers.py @@ -77,7 +77,6 @@ NAV_BAR_ITEMS_TO_HIDE = ( # List of items to hide from navigation bar - "arimports", "pricelists", "supplyorders", ) diff --git a/bika/lims/skins/bika/ploneCustom.css.dtml b/bika/lims/skins/bika/ploneCustom.css.dtml index d1eb952060..d6154d8909 100644 --- a/bika/lims/skins/bika/ploneCustom.css.dtml +++ b/bika/lims/skins/bika/ploneCustom.css.dtml @@ -820,95 +820,6 @@ div.alert { } */ -/* ARImport */ -.icons-on .section-arimports .contenttype-folder{ - background: no-repeat transparent url(&dtml-portal_url;/++resource++bika.lims.images/folder.png); -} -table.arimport { - border-top: 1px solid #dddddd; -} -table.arimport { - width: 100%; - border-collapse: collapse; - border-left: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - border-bottom: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - margin: 1em 0em 1em 0em; -} -table.arimport th { - background: &dtml-globalBackgroundColor;; - border-top: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - border-right: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - font-weight: normal; - padding: 0.1em 0.5em; - text-align: left; -} -table.arimport th.center { - text-align: center; -} -table.arimport .top { - border-left: &dtml-borderWidth; &dtml-borderStyle; &dtml-backgroundColor;; - border-top: &dtml-borderWidth; &dtml-borderStyle; &dtml-backgroundColor; ! important; - border-right: &dtml-borderWidth; &dtml-borderStyle; &dtml-backgroundColor; ! important; - text-align: right ! important; - padding: 0em 0em 1em 0em; -} -table.arimport .listingCheckbox { - text-align: center; -} -table.arimport td.left { - text-align: left; - padding-left: 0.5em; -} -table.arimport td.contact { - text-align: left; -} -table.arimport td { - border-right: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - border-top: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - border-bottom: &dtml-borderWidth; &dtml-borderStyle; &dtml-globalBorderColor;; - padding: 0em 0em; - vertical-align: top; - text-align: center; -} -table.arimport .noborder { - background: transparent;; - border: none -} -table.arimport td.center { - vertical-align: middle; -} -table.arimport a:hover { - text-decoration: underline; -} -table.arimport img { - vertical-align: middle; -} - -table.arimport input { - border: 1px solid &dtml-globalBackgroundColor;; -} - -table.arimport .amount { - text-align: right; - padding-right: 1em; -} -table.arimport .calculated { - text-align: right; - padding-right: 1em; - border: none ! important; - background: &dtml-backgroundColor;; -} -.cb, -table.arimport * .cb { - border: none; - background-color: transparent; -} -table.arimport .dryhead { - background-color: #a6b5db; -} -table.arimport .drybody { - background-color: #dae0f0; -} div.uncertaintydiv { z-index: 100; position: absolute; diff --git a/bika/lims/tests/files/BikaARImportTemplate.csv b/bika/lims/tests/files/BikaARImportTemplate.csv deleted file mode 100644 index a4b17c39ca..0000000000 --- a/bika/lims/tests/files/BikaARImportTemplate.csv +++ /dev/null @@ -1,18 +0,0 @@ -Header,File name,Client name,Client ID,Contact,CC Names - Report,CC Emails - Report,CC Names - Invoice,CC Emails - Invoice,Client Order Number,Client Reference,No of Samples,,,,,,,,,,,,,,,,,,,, -Header Data,BikaARImportTemplate,Happy Hills,HH,Rita Mohale,,,,,,,10,,,,,,,,,,,,,,,,,,,, -Batch Header,title,description,ClientBatchID,ClientBatchComment,BatchLabels,ReturnSampleToClient,,,,,,,,,,,,,,,,,,,,,,,,, -Batch Data,Happy Hills Survey 13,,CC 201506,Routine monthly safety measure,Commercial,1,,,,,,,,,,,,,,,,,,,,,,,,, -Samples,ClientSampleID,SamplingDate,DateSampled,SamplePoint,Activity SampledĀ ,Amount Sampled,Metric,SampleMatrix,SampleType,ContainerType,ReportDryMatter,Priority,Total number of Analyses or Profiles,Price excl Tax,PTM,TH,Mbcheck,Mbcount,Temp,Test,Ca,Enterocnt,pHField,Conductivity,Zn,pH,Ecoli,Fe,Entero,THCaCO3,Cu -Analysis price,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Total Analyses or Profiles,,,,,,,,,,,,,36,,2,2,4,2,3,1,3,2,2,2,1,4,2,2,2,1,1 -Total price excl Tax,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Sample 1,HHS14001,3/9/2014,3/9/2014,Rooikloof Farm,Drilling,1.05,l,Animal feed,Canola,Glass Bottle 500ml,0,Normal,7,,1,,1,,1,,,1,,1,,1,,,1,, -Sample 2,HHS14002,3/9/2014,3/9/2014,Dispatch,Drilling,1.1,l,Water,Lucerne,Canvas bag,0,Normal,3,,,1,,,,,,,,,,1,,,,1, -Sample 3,HHS14003,3/9/2014,3/9/2014,Mixer,Drilling,0.95,l,Fiber,Canola,Micro Glass Bottle 500ml,0,Normal,3,,,,1,,,,,,,,,1,1,,,, -Sample 4,HHS14004,3/9/2014,3/9/2014,Packaging,Drilling,1.03,l,Fiber,Sunflower,Canvas bag,0,Normal,4,,,,,1,1,,1,,,1,,,,,,, -Sample 5,HHS14005,3/9/2014,3/9/2014,Packaging,Drilling,1.08,l,Grain,Canola,Plastic bottle,0,Normal,2,,,,,,,,,,,,1,,,1,,, -Sample 6,HHS14006,3/9/2014,3/9/2014,Bruma Lake,Drilling,0.93,l,Industrial Effluent,Dust,Plastic bottle,0,Normal,4,,,,1,,1,1,,,,,,,1,,,, -Sample 7,HHS14007,3/9/2014,3/9/2014,Shop - BB Supermarket,Drilling,1.01,l,Animal feed,Bran,Plastic bottle,0,Normal,4,,1,,,,,,,,1,,,,,1,,,1 -Sample 8,HHS14008,3/9/2014,3/9/2014,Borehole 12,Drilling,1.06,l,Fiber,Water,Glass bottle,0,Normal,4,,,,1,,,,1,,,,,1,,,1,, -Sample 9,HHS14009,3/9/2014,3/9/2014,Dry stock,Drilling,0.91,l,Sewage,Sunflower,Glass bottle,0,Normal,2,,,,,1,,,1,,,,,,,,,, -Sample 10,HHS14010,3/9/2014,3/9/2014,Packaging,Drilling,0.99,l,Fiber,Soya,Glass Bottle 500ml,0,Normal,3,,,1,,,,,,1,1,,,,,,,, diff --git a/bika/lims/tests/test_ARImport.py b/bika/lims/tests/test_ARImport.py deleted file mode 100644 index d83ebbfacc..0000000000 --- a/bika/lims/tests/test_ARImport.py +++ /dev/null @@ -1,249 +0,0 @@ -# -*- 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. - -import re - -import transaction -from bika.lims.catalog import (CATALOG_ANALYSIS_LISTING, - CATALOG_ANALYSIS_REQUEST_LISTING) -from bika.lims.tests.base import BaseTestCase -from bika.lims.utils import tmpID -from bika.lims.workflow import doActionFor, getCurrentState -from plone.app.testing import (TEST_USER_ID, TEST_USER_NAME, - TEST_USER_PASSWORD, login, setRoles) -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import _createObjectByType - -try: - import unittest2 as unittest -except ImportError: # Python 2.7 - import unittest - - -class TestARImports(BaseTestCase): - def addthing(self, folder, portal_type, **kwargs): - thing = _createObjectByType(portal_type, folder, tmpID()) - thing.unmarkCreationFlag() - thing.edit(**kwargs) - thing._renameAfterCreation() - return thing - - def setUp(self): - super(TestARImports, self).setUp() - setRoles(self.portal, TEST_USER_ID, ['Member', 'LabManager']) - login(self.portal, TEST_USER_NAME) - client = self.addthing( - self.portal.clients, 'Client', title='Happy Hills', ClientID='HH') - self.addthing( - client, 'Contact', Firstname='Rita Mohale', Lastname='Mohale') - self.addthing( - self.portal.bika_setup.bika_sampletypes, 'SampleType', - title='Water', Prefix='H2O') - self.addthing( - self.portal.bika_setup.bika_samplematrices, 'SampleMatrix', - title='Liquids') - self.addthing( - self.portal.bika_setup.bika_samplepoints, 'SamplePoint', - title='Toilet') - self.addthing( - self.portal.bika_setup.bika_containertypes, 'ContainerType', - title='Cup') - a = self.addthing( - self.portal.bika_setup.bika_analysisservices, 'AnalysisService', - title='Ecoli', Keyword="ECO") - b = self.addthing( - self.portal.bika_setup.bika_analysisservices, 'AnalysisService', - title='Salmonella', Keyword="SAL") - c = self.addthing( - self.portal.bika_setup.bika_analysisservices, 'AnalysisService', - title='Color', Keyword="COL") - d = self.addthing( - self.portal.bika_setup.bika_analysisservices, 'AnalysisService', - title='Taste', Keyword="TAS") - self.addthing( - self.portal.bika_setup.bika_analysisprofiles, 'AnalysisProfile', - title='MicroBio', Service=[a.UID(), b.UID()]) - self.addthing( - self.portal.bika_setup.bika_analysisprofiles, 'AnalysisProfile', - title='Properties', Service=[c.UID(), d.UID()]) - - def tearDown(self): - super(TestARImports, self).setUp() - login(self.portal, TEST_USER_NAME) - - def test_complete_valid_batch_import(self): - pc = getToolByName(self.portal, 'portal_catalog') - workflow = getToolByName(self.portal, 'portal_workflow') - client = self.portal.clients.objectValues()[0] - arimport = self.addthing(client, 'ARImport') - arimport.unmarkCreationFlag() - arimport.setFilename("test1.csv") - arimport.setOriginalFile(""" -Header, File name, Client name, Client ID, Contact, CC Names - Report, CC Emails - Report, CC Names - Invoice, CC Emails - Invoice, No of Samples, Client Order Number, Client Reference,, -Header Data, test1.csv, Happy Hills, HH, Rita Mohale, , , , , 10, HHPO-001, ,, -Batch Header, id, title, description, ClientBatchID, ClientBatchComment, BatchLabels, ReturnSampleToClient,,, -Batch Data, B15-0123, New Batch, Optional descr, CC 201506, Just a batch, , TRUE ,,, -Samples, ClientSampleID, SamplingDate,DateSampled,SamplePoint,SampleMatrix,SampleType,ContainerType,ReportDryMatter,Priority,Total number of Analyses or Profiles,Price excl Tax,ECO,SAL,COL,TAS,MicroBio,Properties -Analysis price,,,,,,,,,,,,,, -"Total Analyses or Profiles",,,,,,,,,,,,,9,,, -Total price excl Tax,,,,,,,,,,,,,, -"Sample 1", HHS14001, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 0, Normal, 1, 0, 0,0,0,0,0,1 -"Sample 2", HHS14002, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 0, Normal, 2, 0, 0,0,0,0,1,1 -"Sample 3", HHS14002, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 0, Normal, 4, 0, 1,1,1,1,0,0 -"Sample 4", HHS14002, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 0, Normal, 2, 0, 1,0,0,0,1,0 - """) - - # check that values are saved without errors - arimport.setErrors([]) - arimport.save_header_data() - arimport.save_sample_data() - arimport.create_or_reference_batch() - errors = arimport.getErrors() - if errors: - self.fail("Unexpected errors while saving data: " + str(errors)) - # check that batch was created and linked to arimport without errors - if not pc(portal_type='Batch'): - self.fail("Batch was not created!") - if not arimport.schema['Batch'].get(arimport): - self.fail("Batch was created, but not linked to ARImport.") - - # the workflow scripts use response.write(); silence them - arimport.REQUEST.response.write = lambda x: x - - # check that validation succeeds without any errors - workflow.doActionFor(arimport, 'validate') - state = workflow.getInfoFor(arimport, 'review_state') - if state != 'valid': - errors = arimport.getErrors() - self.fail( - 'Validation failed! %s.Errors: %s' % (arimport.id, errors)) - - # Import objects and verify that they exist - workflow.doActionFor(arimport, 'import') - state = workflow.getInfoFor(arimport, 'review_state') - if state != 'imported': - errors = arimport.getErrors() - self.fail( - 'Importation failed! %s.Errors: %s' % (arimport.id, errors)) - - barc = getToolByName(self.portal, CATALOG_ANALYSIS_REQUEST_LISTING) - ars = barc(portal_type='AnalysisRequest') - if not ars[0].getObject().getContact(): - self.fail('No Contact imported into ar.Contact field.') - l = len(ars) - if l != 4: - self.fail('4 AnalysisRequests were not created! We found %s' % l) - bac = getToolByName(self.portal, CATALOG_ANALYSIS_LISTING) - analyses = bac(portal_type='Analysis') - l = len(analyses) - if l != 12: - self.fail('12 Analysis not found! We found %s' % l) - states = [workflow.getInfoFor(a.getObject(), 'review_state') - for a in analyses] - ars_states = [ar.review_state for ar in ars] - if ars_states != ['sample_due'] * 4: - self.fail('Samples states should all be sample_due, ' - 'but are not!') - if states != ['registered'] * 12: - self.fail('Analysis states should all be registered, but are not!') - - def test_LIMS_2080_correctly_interpret_false_and_blank_values(self): - client = self.portal.clients.objectValues()[0] - arimport = self.addthing(client, 'ARImport') - arimport.unmarkCreationFlag() - arimport.setFilename("test1.csv") - arimport.setOriginalFile(""" -Header, File name, Client name, Client ID, Contact, CC Names - Report, CC Emails - Report, CC Names - Invoice, CC Emails - Invoice, No of Samples, Client Order Number, Client Reference,, -Header Data, test1.csv, Happy Hills, HH, Rita Mohale, , , , , 10, HHPO-001, ,, -Samples, ClientSampleID, SamplingDate,DateSampled,SamplePoint,SampleMatrix,SampleType,ContainerType,ReportDryMatter,Priority,Total number of Analyses or Profiles,Price excl Tax,ECO,SAL,COL,TAS,MicroBio,Properties -Analysis price,,,,,,,,,,,,,, -"Total Analyses or Profiles",,,,,,,,,,,,,9,,, -Total price excl Tax,,,,,,,,,,,,,, -"Sample 1", HHS14001, 3/9/2014, 3/9/2014, , , Water, Cup, 0, Normal, 1, 0, 0,0,0,0,0,1 -"Sample 2", HHS14002, 3/9/2014, 3/9/2014, , , Water, Cup, 0, Normal, 2, 0, 0,0,0,0,1,1 -"Sample 3", HHS14002, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 1, Normal, 4, 0, 1,1,1,1,0,0 -"Sample 4", HHS14002, 3/9/2014, 3/9/2014, Toilet, Liquids, Water, Cup, 1, Normal, 2, 0, 1,0,0,0,1,0 - """) - - # check that values are saved without errors - arimport.setErrors([]) - arimport.save_header_data() - arimport.save_sample_data() - errors = arimport.getErrors() - if errors: - self.fail("Unexpected errors while saving data: " + str(errors)) - transaction.commit() - browser = self.getBrowser( - username=TEST_USER_NAME, - password=TEST_USER_PASSWORD, - loggedIn=True) - - doActionFor(arimport, 'validate') - c_state = getCurrentState(arimport) - self.assertTrue( - c_state == 'valid', - "ARrimport in 'invalid' state after it has been transitioned to " - "'valid'.") - browser.open(arimport.absolute_url() + "/edit") - content = browser.contents - re.match( - '', - content) - if len(re.findall('<.*selected.*Toilet', content)) != 2: - self.fail("Should be two empty SamplePoints, and two with values") - if len(re.findall('<.*selected.*Liquids', content)) != 2: - self.fail("Should be two empty Matrix fields, and two with values") - - def test_LIMS_2081_post_edit_fails_validation_gracefully(self): - client = self.portal.clients.objectValues()[0] - arimport = self.addthing(client, 'ARImport') - arimport.unmarkCreationFlag() - arimport.setFilename("test1.csv") - arimport.setOriginalFile(""" -Header, File name, Client name, Client ID, Contact, CC Names - Report, CC Emails - Report, CC Names - Invoice, CC Emails - Invoice, No of Samples, Client Order Number, Client Reference,, -Header Data, test1.csv, Happy Hills, HH, Rita Mohale, , , , , 10, HHPO-001, ,, -Samples, ClientSampleID, SamplingDate,DateSampled,SamplePoint,SampleMatrix,SampleType,ContainerType,ReportDryMatter,Priority,Total number of Analyses or Profiles,Price excl Tax,ECO,SAL,COL,TAS,MicroBio,Properties -Analysis price,,,,,,,,,,,,,, -"Total Analyses or Profiles",,,,,,,,,,,,,9,,, -Total price excl Tax,,,,,,,,,,,,,, -"Sample 1", HHS14001, 3/9/2014, 3/9/2014, , , Water, Cup, 0, Normal, 1, 0, 0,0,0,0,0,1 - """) - - # check that values are saved without errors - arimport.setErrors([]) - arimport.save_header_data() - arimport.save_sample_data() - arimport.create_or_reference_batch() - errors = arimport.getErrors() - if errors: - self.fail("Unexpected errors while saving data: " + str(errors)) - transaction.commit() - browser = self.getBrowser(loggedIn=True) - browser.open(arimport.absolute_url() + "/edit") - browser.getControl(name="ClientReference").value = 'test_reference' - browser.getControl(name="form.button.save").click() - if 'test_reference' not in browser.contents: - self.fail('Failed to modify ARImport object (Client Reference)') - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestARImports)) - return suite diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index d2ba2d0a22..9a87198602 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -43,6 +43,7 @@ TYPES_TO_REMOVE = [ + "ARImport", "SamplesFolder", "BikaCache", # invoices were removed in upgrade step 1.3.0 @@ -50,6 +51,10 @@ "InvoiceFolder", ] +WFS_TO_REMOVE = [ + "bika_arimport_workflow", +] + JAVASCRIPTS_TO_REMOVE = [ # moved from lims -> core "++resource++senaite.lims.jquery.js/jquery-2.2.4.min.js", @@ -783,4 +788,10 @@ def remove_stale_type_registrations(portal): if t in pt.objectIds(): pt.manage_delObjects(t) + wf_tool = portal.portal_workflow + for wf in WFS_TO_REMOVE: + if wf in wf_tool: + logger.info("Removing Workflow '{}'".format(wf)) + wf_tool.manage_delObjects(wf) + logger.info("Removing stale type registrations [DONE]") From 055e44d77919ca91b6b54987a337aa7171b5dee1 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 22:04:37 +0100 Subject: [PATCH 22/28] Removed stale imports for sample + samplepartition --- bika/lims/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bika/lims/__init__.py b/bika/lims/__init__.py index 3a63bb8ec2..14815547ae 100644 --- a/bika/lims/__init__.py +++ b/bika/lims/__init__.py @@ -121,10 +121,8 @@ def initialize(context): from content.rejectanalysis import RejectAnalysis # noqa from content.report import Report # noqa from content.reportfolder import ReportFolder # noqa - from content.sample import Sample # noqa from content.samplecondition import SampleCondition # noqa from content.samplematrix import SampleMatrix # noqa - from content.samplepartition import SamplePartition # noqa from content.samplepoint import SamplePoint # noqa from content.sampletype import SampleType # noqa from content.samplingdeviation import SamplingDeviation # noqa From 71cbc975cd44573927e48a37e6993e6db13342cc Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 22:10:22 +0100 Subject: [PATCH 23/28] Removed stale import from test --- bika/lims/tests/doctests/AnalysisTurnaroundTime.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/bika/lims/tests/doctests/AnalysisTurnaroundTime.rst b/bika/lims/tests/doctests/AnalysisTurnaroundTime.rst index a0201fe147..e48373f4e4 100644 --- a/bika/lims/tests/doctests/AnalysisTurnaroundTime.rst +++ b/bika/lims/tests/doctests/AnalysisTurnaroundTime.rst @@ -17,7 +17,6 @@ Needed Imports: >>> from bika.lims.api.analysis import get_formatted_interval >>> from bika.lims.api.analysis import is_out_of_range >>> from bika.lims.content.analysisrequest import AnalysisRequest - >>> from bika.lims.content.sample import Sample >>> from bika.lims.utils.analysisrequest import create_analysisrequest >>> from bika.lims.utils import tmpID >>> from bika.lims.workflow import doActionFor From 3796dd54ac3673023722474e07101df744c45996 Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 22:12:13 +0100 Subject: [PATCH 24/28] Removed stale import from API doctest --- bika/lims/tests/doctests/API_analysis.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/bika/lims/tests/doctests/API_analysis.rst b/bika/lims/tests/doctests/API_analysis.rst index d699a012b9..3524dddd07 100644 --- a/bika/lims/tests/doctests/API_analysis.rst +++ b/bika/lims/tests/doctests/API_analysis.rst @@ -20,7 +20,6 @@ Needed Imports: >>> from bika.lims.api.analysis import get_formatted_interval >>> from bika.lims.api.analysis import is_out_of_range >>> from bika.lims.content.analysisrequest import AnalysisRequest - >>> from bika.lims.content.sample import Sample >>> from bika.lims.utils.analysisrequest import create_analysisrequest >>> from bika.lims.utils import tmpID >>> from bika.lims.workflow import doActionFor From 880873863dba151aa36b703019192823c205377f Mon Sep 17 00:00:00 2001 From: Ramon Bartl Date: Wed, 12 Feb 2020 22:14:49 +0100 Subject: [PATCH 25/28] Removed stale import from WS doctest --- bika/lims/tests/doctests/WorksheetApplyTemplate.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/bika/lims/tests/doctests/WorksheetApplyTemplate.rst b/bika/lims/tests/doctests/WorksheetApplyTemplate.rst index 82c9bbc4c7..ebe59b3bc9 100644 --- a/bika/lims/tests/doctests/WorksheetApplyTemplate.rst +++ b/bika/lims/tests/doctests/WorksheetApplyTemplate.rst @@ -33,7 +33,6 @@ Needed Imports: >>> from AccessControl.PermissionRole import rolesForPermissionOn >>> from bika.lims import api >>> from bika.lims.content.analysisrequest import AnalysisRequest - >>> from bika.lims.content.sample import Sample >>> from bika.lims.utils.analysisrequest import create_analysisrequest >>> from bika.lims.utils import tmpID >>> from bika.lims.workflow import doActionFor From 7d92642d6486af60f7a8e41275e8a7d3ce12b98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 21 Feb 2020 11:45:02 +0100 Subject: [PATCH 26/28] We don't remove samplingrounds-like objects: keep legacy interfaces --- bika/lims/interfaces/__init__.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 48f167b4b8..02ada62f3b 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -1062,12 +1062,24 @@ def getCategoryTitle(self): """ -# Legacy types to remove after 1.3.3 class IARImportFolder(Interface): """Marker interface for a folder that contains ARImports + TODO: Legacy type. Remove after 1.3.3 """ class IARImport(Interface): """Marker interface for an ARImport + TODO: Legacy type. Remove after 1.3.3 + """ + +class ISamplingRoundTemplates(Interface): + """Marker interface for Sampling Round Templates + TODO: Legacy type. Remove after 1.3.3 + """ + + +class ISamplingRoundTemplate(Interface): + """Marker interface for a Sampling Round Template + TODO: Legacy type. Remove after 1.3.3 """ From 675606d02ed3be0b76493719d065cb47e52909ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 21 Feb 2020 12:16:49 +0100 Subject: [PATCH 27/28] Fix Traceback when removing samplingrounds setup's folder Traceback (innermost last): Module ZPublisher.Publish, line 138, in publish Module ZPublisher.mapply, line 77, in mapply Module ZPublisher.Publish, line 48, in call_object Module Products.GenericSetup.tool, line 1053, in manage_doUpgrades Module Products.GenericSetup.upgrade, line 166, in doStep Module bika.lims.upgrade, line 68, in wrap_func_args Module bika.lims.upgrade.v01_03_003, line 393, in upgrade Module bika.lims.upgrade.v01_03_003, line 892, in remove_samplingrounds UnboundLocalError: local variable 'obj' referenced before assignment --- bika/lims/interfaces/__init__.py | 1 + bika/lims/upgrade/v01_03_003.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 02ada62f3b..73bc202263 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -1073,6 +1073,7 @@ class IARImport(Interface): TODO: Legacy type. Remove after 1.3.3 """ + class ISamplingRoundTemplates(Interface): """Marker interface for Sampling Round Templates TODO: Legacy type. Remove after 1.3.3 diff --git a/bika/lims/upgrade/v01_03_003.py b/bika/lims/upgrade/v01_03_003.py index 8cfd2fc0b8..93872d53c2 100644 --- a/bika/lims/upgrade/v01_03_003.py +++ b/bika/lims/upgrade/v01_03_003.py @@ -889,7 +889,7 @@ def remove_actions(action_ids, obj): # remove parent objects if hasattr(parent, "unindexObject"): parent.unindexObject() - logger.info("Removing object '{}'".format(api.get_path(obj))) + logger.info("Removing object '{}'".format(api.get_path(parent))) setup._delOb(oid) except KeyError: pass From 40a915b70dbe5dd94d5bcdabde02fbff70ffc0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 21 Feb 2020 12:24:52 +0100 Subject: [PATCH 28/28] Prevent Traceback when upgrading site Traceback (innermost last): Module ZPublisher.Publish, line 138, in publish Module ZPublisher.mapply, line 77, in mapply Module ZPublisher.Publish, line 48, in call_object Module Products.GenericSetup.tool, line 1053, in manage_doUpgrades Module Products.GenericSetup.upgrade, line 166, in doStep Module bika.lims.upgrade, line 68, in wrap_func_args Module bika.lims.upgrade.v01_03_003, line 393, in upgrade Module bika.lims.upgrade.v01_03_003, line 892, in remove_samplingrounds Module bika.lims.api, line 554, in get_path Module bika.lims.api, line 240, in get_object Module bika.lims.api, line 207, in fail APIError: is not supported. --- bika/lims/controlpanel/bika_samplingrounds.py | 35 ++++++++++++++++ bika/lims/controlpanel/bika_srtemplates.py | 41 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 bika/lims/controlpanel/bika_samplingrounds.py create mode 100644 bika/lims/controlpanel/bika_srtemplates.py diff --git a/bika/lims/controlpanel/bika_samplingrounds.py b/bika/lims/controlpanel/bika_samplingrounds.py new file mode 100644 index 0000000000..da97ad498b --- /dev/null +++ b/bika/lims/controlpanel/bika_samplingrounds.py @@ -0,0 +1,35 @@ +# -*- 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 plone.dexterity.content import Container +from plone.supermodel import model +from zope.interface import implements + + +# TODO: Legacy type. Remove after 1.3.3 +class ISamplingRounds(model.Schema): + """ A Sampling Rounds container. + """ + + +# TODO: Legacy type. Remove after 1.3.3 +class SamplingRounds(Container): + implements(ISamplingRounds) + displayContentsTab = False diff --git a/bika/lims/controlpanel/bika_srtemplates.py b/bika/lims/controlpanel/bika_srtemplates.py new file mode 100644 index 0000000000..948d10a4ff --- /dev/null +++ b/bika/lims/controlpanel/bika_srtemplates.py @@ -0,0 +1,41 @@ +# -*- 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 Products.ATContentTypes.content import schemata +from Products.Archetypes import atapi +from plone.app.folder.folder import ATFolder +from plone.app.folder.folder import ATFolderSchema +from zope.interface.declarations import implements + +from bika.lims.config import PROJECTNAME +from bika.lims.interfaces import ISamplingRoundTemplates + +schema = ATFolderSchema.copy() + + +# TODO: Legacy type. Remove after 1.3.3 +class SRTemplates(ATFolder): + implements(ISamplingRoundTemplates) + displayContentsTab = False + schema = schema + + +schemata.finalizeATCTSchema(schema, folderish = True, moveDiscussion = False) +atapi.registerType(SRTemplates, PROJECTNAME)