Passed
Push — 2.x ( b18a4c...aa4f97 )
by Jordi
06:46
created

CreateWorksheetModal.get_analysts()   A

Complexity

Conditions 3

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 21
rs 9.9
c 0
b 0
f 0
cc 3
nop 1
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2025 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from bika.lims import api
22
from bika.lims import senaiteMessageFactory as _
23
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
24
from senaite.core.browser.modals import Modal
25
from senaite.core.catalog import SETUP_CATALOG
26
from six import string_types
27
28
29
class CreateWorksheetModal(Modal):
30
    """Modal form handler that allows to assign all analyses to a new worksheet
31
    """
32
    template = ViewPageTemplateFile("templates/create_worksheet.pt")
33
34
    def __init__(self, context, request):
35
        super(CreateWorksheetModal, self).__init__(context, request)
36
37
    def __call__(self):
38
        if self.request.form.get("submitted", False):
39
            return self.handle_submit(REQUEST=self.request)
40
        return self.template()
41
42
    def add_status_message(self, message, level="info"):
43
        """Set a portal status message
44
        """
45
        return self.context.plone_utils.addPortalMessage(message, level)
46
47
    def get_selected_samples(self):
48
        """Return selected samples
49
50
        :returns: selected Samples
51
        """
52
        return map(api.get_object, self.uids)
53
54
    def handle_submit(self, REQUEST=None):
55
        """Extract categories from request and create worksheet
56
        """
57
        analyst = self.request.form.get("analyst")
58
        categories = self.request.form.get("categories", [])
59
        template = self.request.form.get("template")
60
        samples = list(map(api.get_object, self.uids))
61
62
        if isinstance(categories, string_types):
63
            categories = [categories]
64
        # filter out non-UIDs
65
        categories = filter(api.is_uid, categories)
66
67
        worksheet = self.create_worksheet_for(
68
            samples, analyst, categories, template)
69
        self.add_status_message(
70
            _("Created worksheet %s" % api.get_id(worksheet)), level="info")
71
        # redirect to the new worksheet
72
        return api.get_url(worksheet)
73
74
    def create_worksheet_for(self, samples, analyst, categories, template):
75
        """Create a new worksheet
76
77
        The new worksheet contains the analyses of all samples which match the
78
        are in the given categories.
79
80
        :param samples: Sample obejects or UIDs
81
        :param categories: Category objects or UIDs
82
        :param template: Worksheet template
83
        :returns: new created Workshett
84
        """
85
        categories = map(api.get_object, categories)
86
        analyses = []
87
        for sample in samples:
88
            for analysis in sample.getAnalyses(full_objects=True):
89
                if analysis.getCategory() not in categories:
90
                    continue
91
                analyses.append(analysis)
92
93
        # create the new worksheet
94
        ws = api.create(self.worksheet_folder, "Worksheet")
95
        ws.setAnalyst(analyst)
96
        ws.addAnalyses(analyses)
97
        ws.applyWorksheetTemplate(api.get_object(template, None))
98
        ws.setResultsLayout(self.worksheet_layout)
99
        return ws
100
101
    @property
102
    def worksheet_folder(self):
103
        """Return the worksheet root folder
104
        """
105
        portal = api.get_portal()
106
        return portal.restrictedTraverse("worksheets")
107
108
    @property
109
    def worksheet_layout(self):
110
        """Return the configured workheet layout
111
        """
112
        setup = api.get_setup()
113
        return setup.getWorksheetLayout()
114
115
    def get_analysis_categories(self):
116
        """Return analysis categories of the selected samples
117
118
        :returns: List available categories for the selected samples
119
        """
120
        categories = []
121
        for sample in self.get_selected_samples():
122
            for analysis in sample.getAnalyses(full_objects=True):
123
                # only consider unassigned analyses
124
                if api.get_workflow_status_of(analysis) != "unassigned":
125
                    continue
126
                # get the category of the analysis
127
                category = analysis.getCategory()
128
                if category in categories:
129
                    continue
130
                categories.append(category)
131
132
        categories = list(map(self.get_category_info,
133
                          sorted(categories, key=lambda c: c.getSortKey())))
134
        return categories
135
136
    def get_category_info(self, category):
137
        """Extract category information for template
138
139
        :returns: Dictionary of category information for the page template
140
        """
141
        return {
142
            "title": api.get_title(category),
143
            "uid": api.get_uid(category),
144
            "obj": category,
145
        }
146
147
    def get_analysts(self):
148
        """Returns all analyst users
149
150
        This function searches for users which have at least the Analyst role,
151
        and prepares a list of dictionaries for each user containing the
152
        username and fullname.
153
154
        :returns: List of analyst data dictionaries
155
        """
156
        #
157
        users = api.get_users_by_roles(["Manager", "LabManager", "Analyst"])
158
        analysts = []
159
        for user in users:
160
            username = user.getUserName()
161
            fullname = api.get_user_fullname(username)
162
            analysts.append({
163
                "username": username,
164
                "fullname": fullname or username,
165
            })
166
        # sort by fulname
167
        return sorted(analysts, key=lambda x: x.get("fullname").lower())
168
169
    def get_worksheet_templates(self):
170
        """Returns all WS templates
171
172
        This function searches for worksheet templates and prepares a list of
173
        dictionaries for each template containing the UID and the title.
174
175
        :returns_ List of worksheet template data dictionaries
176
        """
177
        templates = [{"uid": "", "title": ""}]
178
        query = {
179
            "portal_type": "WorksheetTemplate",
180
            "is_active": True,
181
            "sort_on": "sortable_title",
182
        }
183
        results = api.search(query, SETUP_CATALOG)
184
        for brain in results:
185
            templates.append({
186
                "uid": api.get_uid(brain),
187
                "title": api.get_title(brain)
188
            })
189
        return templates
190