| Conditions | 20 |
| Total Lines | 142 |
| Code Lines | 64 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like build.bika.lims.browser.fields.aranalysesfield.ARAnalysesField.set() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # -*- coding: utf-8 -*- |
||
| 75 | def set(self, instance, items, prices=None, specs=None, **kwargs): |
||
| 76 | """Set/Assign Analyses to this AR |
||
| 77 | |||
| 78 | :param items: List of Analysis objects/brains, AnalysisService |
||
| 79 | objects/brains and/or Analysis Service uids |
||
| 80 | :type items: list |
||
| 81 | :param prices: Mapping of AnalysisService UID -> price |
||
| 82 | :type prices: dict |
||
| 83 | :param specs: List of AnalysisService UID -> Result Range mappings |
||
| 84 | :type specs: list |
||
| 85 | :returns: list of new assigned Analyses |
||
| 86 | """ |
||
| 87 | # This setter returns a list of new set Analyses |
||
| 88 | new_analyses = [] |
||
| 89 | |||
| 90 | # Current assigned analyses |
||
| 91 | analyses = instance.objectValues("Analysis") |
||
| 92 | |||
| 93 | # Analyses which are in a non-open state must be retained, except those |
||
| 94 | # that are in a registered state (the sample has not been received) |
||
| 95 | non_open_analyses = filter(lambda an: not an.isOpen(), analyses) |
||
| 96 | non_open_analyses = filter(lambda an: api.get_workflow_status_of(an) |
||
| 97 | != "registered", non_open_analyses) |
||
| 98 | |||
| 99 | # Prevent removing all analyses |
||
| 100 | # |
||
| 101 | # N.B.: Non-open analyses are rendered disabled in the HTML form. |
||
| 102 | # Therefore, their UIDs are not included in the submitted UIDs. |
||
| 103 | if not items and not non_open_analyses: |
||
| 104 | logger.warn("Not allowed to remove all Analyses from AR.") |
||
| 105 | return new_analyses |
||
| 106 | |||
| 107 | # Bail out if the items is not a list type |
||
| 108 | if not isinstance(items, (list, tuple)): |
||
| 109 | raise TypeError( |
||
| 110 | "Items parameter must be a tuple or list, got '{}'".format( |
||
| 111 | type(items))) |
||
| 112 | |||
| 113 | # Bail out if the AR is inactive |
||
| 114 | if not api.is_active(instance): |
||
| 115 | raise Unauthorized("Inactive ARs can not be modified" |
||
| 116 | .format(AddAnalysis)) |
||
| 117 | |||
| 118 | # Bail out if the user has not the right permission |
||
| 119 | sm = getSecurityManager() |
||
| 120 | if not sm.checkPermission(AddAnalysis, instance): |
||
| 121 | raise Unauthorized("You do not have the '{}' permission" |
||
| 122 | .format(AddAnalysis)) |
||
| 123 | |||
| 124 | # Convert the items to a valid list of AnalysisServices |
||
| 125 | services = filter(None, map(self._to_service, items)) |
||
| 126 | |||
| 127 | # Calculate dependencies |
||
| 128 | # FIXME Infinite recursion error possible here, if the formula includes |
||
| 129 | # the Keyword of the Service that includes the Calculation |
||
| 130 | dependencies = map(lambda s: s.getServiceDependencies(), services) |
||
| 131 | dependencies = list(itertools.chain.from_iterable(dependencies)) |
||
| 132 | |||
| 133 | # Merge dependencies and services |
||
| 134 | services = set(services + dependencies) |
||
| 135 | |||
| 136 | # Modify existing AR specs with new form values of selected analyses. |
||
| 137 | self._update_specs(instance, specs) |
||
| 138 | |||
| 139 | # CREATE/MODIFY ANALYSES |
||
| 140 | |||
| 141 | for service in services: |
||
| 142 | keyword = service.getKeyword() |
||
| 143 | |||
| 144 | # Create the Analysis if it doesn't exist |
||
| 145 | if shasattr(instance, keyword): |
||
| 146 | analysis = instance._getOb(keyword) |
||
| 147 | else: |
||
| 148 | analysis = create_analysis(instance, service) |
||
| 149 | new_analyses.append(analysis) |
||
| 150 | |||
| 151 | # Set the price of the Analysis |
||
| 152 | self._update_price(analysis, service, prices) |
||
| 153 | |||
| 154 | # DELETE ANALYSES |
||
| 155 | |||
| 156 | # Service UIDs |
||
| 157 | service_uids = map(api.get_uid, services) |
||
| 158 | |||
| 159 | # Analyses IDs to delete |
||
| 160 | delete_ids = [] |
||
| 161 | |||
| 162 | # Assigned Attachments |
||
| 163 | assigned_attachments = [] |
||
| 164 | |||
| 165 | for analysis in analyses: |
||
| 166 | service_uid = analysis.getServiceUID() |
||
| 167 | |||
| 168 | # Skip if the Service is selected |
||
| 169 | if service_uid in service_uids: |
||
| 170 | continue |
||
| 171 | |||
| 172 | # Skip non-open Analyses |
||
| 173 | if analysis in non_open_analyses: |
||
| 174 | continue |
||
| 175 | |||
| 176 | # Remember assigned attachments |
||
| 177 | # https://github.com/senaite/senaite.core/issues/1025 |
||
| 178 | assigned_attachments.extend(analysis.getAttachment()) |
||
| 179 | analysis.setAttachment([]) |
||
| 180 | |||
| 181 | # If it is assigned to a worksheet, unassign it before deletion. |
||
| 182 | worksheet = analysis.getWorksheet() |
||
| 183 | if worksheet: |
||
| 184 | worksheet.removeAnalysis(analysis) |
||
| 185 | |||
| 186 | # Unset the partition reference |
||
| 187 | # TODO Remove in >v1.3.0 - This is kept for backwards-compatibility |
||
| 188 | part = analysis.getSamplePartition() |
||
| 189 | if part: |
||
| 190 | # From this partition, remove the reference to the current |
||
| 191 | # analysis that is going to be removed to prevent inconsistent |
||
| 192 | # states (Sample Partitions referencing to Analyses that do not |
||
| 193 | # exist anymore |
||
| 194 | an_uid = api.get_uid(analysis) |
||
| 195 | part_ans = part.getAnalyses() or [] |
||
| 196 | part_ans = filter( |
||
| 197 | lambda an: api.get_uid(an) != an_uid, part_ans) |
||
|
|
|||
| 198 | part.setAnalyses(part_ans) |
||
| 199 | # Unset the Analysis-to-Partition reference |
||
| 200 | analysis.setSamplePartition(None) |
||
| 201 | delete_ids.append(analysis.getId()) |
||
| 202 | |||
| 203 | if delete_ids: |
||
| 204 | # Note: subscriber might promote the AR |
||
| 205 | instance.manage_delObjects(ids=delete_ids) |
||
| 206 | |||
| 207 | # Remove orphaned attachments |
||
| 208 | for attachment in assigned_attachments: |
||
| 209 | # only delete attachments which are no further linked |
||
| 210 | if not attachment.getLinkedAnalyses(): |
||
| 211 | logger.info( |
||
| 212 | "Deleting attachment: {}".format(attachment.getId())) |
||
| 213 | attachment_id = api.get_id(attachment) |
||
| 214 | api.get_parent(attachment).manage_delObjects(attachment_id) |
||
| 215 | |||
| 216 | return new_analyses |
||
| 217 | |||
| 301 |