Passed
Push — 2.x ( 0ba30f...7ab1ba )
by Jordi
08:49
created

EditForm.update_partition_selectors()   A

Complexity

Conditions 5

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 28
rs 9.1832
c 0
b 0
f 0
cc 5
nop 2
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-2024 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import re
22
23
from bika.lims import api
24
from senaite.core.browser.form.adapters import EditFormAdapterBase
25
from senaite.core.catalog import SETUP_CATALOG
26
from senaite.core.interfaces import ISampleTemplate
27
28
RX1 = r"\d+\.(widgets)\.(part_id$)"
29
RX2 = r"\.(widgets)\.(part_id$)"
30
31
32
class EditForm(EditFormAdapterBase):
33
    """Edit form adapter for DX Sample Template
34
    """
35
    def __init__(self, context, request):
36
        super(EditForm, self).__init__(context, request)
37
38
    def initialized(self, data):
39
        # register callbacks
40
        self.add_callback("body",
41
                          "datagrid:row_added",
42
                          "on_partition_added")
43
        self.add_callback("body",
44
                          "datagrid:row_removed",
45
                          "on_partition_removed")
46
        # handle additional rendered row on edit view
47
        if self.get_current_partition_count(data) > 1:
48
            self.on_partition_added(data)
49
        return self.data
50
51
    def modified(self, data):
52
        return self.data
53
54
    def added(self, data):
55
        # We need to update the partition selectors when rows get rendered,
56
        # e.g. in categories.
57
        self.update_partition_selectors(data)
58
        return self.data
59
60
    def callback(self, data):
61
        name = data.get("name")
62
        if not name:
63
            return
64
        method = getattr(self, name, None)
65
        if not callable(method):
66
            return
67
        return method(data)
68
69
    def update_partition_selectors(self, data):
70
        """Update all service partition selectors with the current settings
71
        """
72
        # Prepare the options list
73
        options = [{"title": "", "value": ""}]
74
        options.extend(
75
            map(lambda o: dict(title=o, value=o),
76
                self.get_current_partition_ids(data, only_numbered=True)))
77
78
        # get the current selected service settings of the template (includes
79
        # partition/hidden settings)
80
        services = self.get_current_service_settings()
81
82
        # iterate over all service UIDs to fill the partition selectors with
83
        # the current (unsaved) partition scheme
84
        for uid in self.get_all_service_uids():
85
            # field name used in the listing widget for the partition select
86
            fieldname = "Partition.{}:records".format(uid)
87
            # current selected service settings
88
            selected = services.get(uid)
89
            # check if we have a partition assigned to this service
90
            part_id = None
91
            if selected:
92
                part_id = selected.get("part_id")
93
            # update the partition select box with the current settings
94
            self.add_update_field(fieldname, {
95
                "selected": [part_id] if part_id else [],
96
                "options": options})
97
98
    def get_current_service_settings(self):
99
        """Get the current service settings
100
        """
101
        # We are probably on the ++add++ form and have no proper context
102
        if not ISampleTemplate.providedBy(self.context):
103
            return {}
104
105
        # Get the current service settings of the template
106
        services = {}
107
        for service in self.context.getRawServices():
108
            uid = service.get("uid")
109
            services[uid] = service
110
111
        return services
112
113
    def get_all_service_uids(self):
114
        """Return the UIDs of all services
115
        """
116
        query = {
117
            "portal_type": "AnalysisService",
118
            "is_active": True,
119
            "sort_on": "sortable_title",
120
            "sort_order": "ascending",
121
        }
122
        results = api.search(query, SETUP_CATALOG)
123
        return list(map(api.get_uid, results))
124
125
    def get_current_partition_ids(self, data, only_numbered=False):
126
        """Get the current unique partition IDs from the request
127
128
        :returns: list of unique parition IDs
129
        """
130
        form = data.get("form")
131
132
        if (only_numbered):
133
            # filter only partition keys that are numbered (without AA/TT keys)
134
            partitions = [(k, v) for k, v in form.items() if re.search(RX1, k)]
135
        else:
136
            partitions = [(k, v) for k, v in form.items() if re.search(RX2, k)]
137
        return list(set(dict(partitions).values()))
138
139
    def get_current_partition_count(self, data):
140
        """Count the current unique partition IDs
141
142
        :returns: Number of rows containing a unique Partition ID
143
        """
144
        unique_ids = self.get_current_partition_ids(data)
145
        return len(unique_ids)
146
147
    def on_partition_added(self, data):
148
        """Handle new partition rows
149
        """
150
        count = self.get_current_partition_count(data)
151
        # we just want to get the next ID right
152
        self.add_update_field(
153
            "form.widgets.partitions.AA.widgets.part_id",
154
            "part-{}".format(count + 1))
155
        return self.data
156
157
    def on_partition_removed(self, data):
158
        """Handle removed partition rows
159
        """
160
        count = self.get_current_partition_count(data)
161
        # we just want to get the next ID right
162
        self.add_update_field(
163
            "form.widgets.partitions.AA.widgets.part_id",
164
            "part-{}".format(count))
165
        return self.data
166