| Conditions | 16 |
| Total Lines | 125 |
| Code Lines | 58 |
| 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 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 -*- |
||
| 107 | def set(self, instance, items, prices=None, specs=None, **kwargs): |
||
| 108 | """Set/Assign Analyses to this AR |
||
| 109 | |||
| 110 | :param items: List of Analysis objects/brains, AnalysisService |
||
| 111 | objects/brains and/or Analysis Service uids |
||
| 112 | :type items: list |
||
| 113 | :param prices: Mapping of AnalysisService UID -> price |
||
| 114 | :type prices: dict |
||
| 115 | :param specs: List of AnalysisService UID -> Result Range Record mappings |
||
| 116 | :type specs: list |
||
| 117 | :returns: list of new assigned Analyses |
||
| 118 | """ |
||
| 119 | |||
| 120 | # This setter returns a list of new set Analyses |
||
| 121 | new_analyses = [] |
||
| 122 | |||
| 123 | # Prevent removing all Analyses |
||
| 124 | if not items: |
||
| 125 | logger.warn("Not allowed to remove all Analyses from AR.") |
||
| 126 | return new_analyses |
||
| 127 | |||
| 128 | # Bail out if the items is not a list type |
||
| 129 | if not isinstance(items, (list, tuple)): |
||
| 130 | raise TypeError( |
||
| 131 | "Items parameter must be a tuple or list, got '{}'".format( |
||
| 132 | type(items))) |
||
| 133 | |||
| 134 | # Bail out if the AR in frozen state |
||
| 135 | if self._is_frozen(instance): |
||
| 136 | raise ValueError( |
||
| 137 | "Analyses can not be modified for inactive/verified ARs") |
||
| 138 | |||
| 139 | # Convert the items to a valid list of AnalysisServices |
||
| 140 | services = filter(None, map(self._to_service, items)) |
||
| 141 | |||
| 142 | # Calculate dependencies |
||
| 143 | # FIXME Infinite recursion error possible here, if the formula includes |
||
| 144 | # the Keyword of the Service that includes the Calculation |
||
| 145 | dependencies = map(lambda s: s.getServiceDependencies(), services) |
||
| 146 | dependencies = list(itertools.chain.from_iterable(dependencies)) |
||
| 147 | |||
| 148 | # Merge dependencies and services |
||
| 149 | services = set(services + dependencies) |
||
| 150 | |||
| 151 | # Service UIDs |
||
| 152 | service_uids = map(api.get_uid, services) |
||
| 153 | |||
| 154 | # Modify existing AR specs with new form values of selected analyses. |
||
| 155 | self._update_specs(instance, specs) |
||
| 156 | |||
| 157 | for service in services: |
||
| 158 | keyword = service.getKeyword() |
||
| 159 | |||
| 160 | # Create the Analysis if it doesn't exist |
||
| 161 | if shasattr(instance, keyword): |
||
| 162 | analysis = instance._getOb(keyword) |
||
| 163 | else: |
||
| 164 | # TODO Entry point for interims assignment and Calculation |
||
| 165 | # decoupling from Analysis. See comments PR#593 |
||
| 166 | analysis = create_analysis(instance, service) |
||
| 167 | # TODO Remove when the `create_analysis` function supports this |
||
| 168 | # Set the interim fields only for new created Analysis |
||
| 169 | self._update_interims(analysis, service) |
||
| 170 | new_analyses.append(analysis) |
||
| 171 | |||
| 172 | # Set the price of the Analysis |
||
| 173 | self._update_price(analysis, service, prices) |
||
| 174 | |||
| 175 | # delete analyses |
||
| 176 | delete_ids = [] |
||
| 177 | assigned_attachments = [] |
||
| 178 | |||
| 179 | for analysis in instance.objectValues('Analysis'): |
||
| 180 | service_uid = analysis.getServiceUID() |
||
| 181 | |||
| 182 | # Skip assigned Analyses |
||
| 183 | if service_uid in service_uids: |
||
| 184 | continue |
||
| 185 | |||
| 186 | # Skip Analyses in frozen states |
||
| 187 | if self._is_frozen(analysis, "retract"): |
||
| 188 | logger.warn("Inactive/verified/retracted Analyses can not be " |
||
| 189 | "removed.") |
||
| 190 | continue |
||
| 191 | |||
| 192 | # Remember assigned attachments |
||
| 193 | # https://github.com/senaite/senaite.core/issues/1025 |
||
| 194 | assigned_attachments.extend(analysis.getAttachment()) |
||
| 195 | analysis.setAttachment([]) |
||
| 196 | |||
| 197 | # If it is assigned to a worksheet, unassign it before deletion. |
||
| 198 | if self._is_assigned_to_worksheet(analysis): |
||
| 199 | backrefs = self._get_assigned_worksheets(analysis) |
||
| 200 | ws = backrefs[0] |
||
| 201 | ws.removeAnalysis(analysis) |
||
| 202 | |||
| 203 | # Unset the partition reference |
||
| 204 | part = analysis.getSamplePartition() |
||
| 205 | if part: |
||
| 206 | # From this partition, remove the reference to the current |
||
| 207 | # analysis that is going to be removed to prevent inconsistent |
||
| 208 | # states (Sample Partitions referencing to Analyses that do not |
||
| 209 | # exist anymore |
||
| 210 | an_uid = api.get_uid(analysis) |
||
| 211 | part_ans = part.getAnalyses() or [] |
||
| 212 | part_ans = filter(lambda an: api.get_uid(an) != an_uid, part_ans) |
||
|
|
|||
| 213 | part.setAnalyses(part_ans) |
||
| 214 | # Unset the Analysis-to-Partition reference |
||
| 215 | analysis.setSamplePartition(None) |
||
| 216 | delete_ids.append(analysis.getId()) |
||
| 217 | |||
| 218 | if delete_ids: |
||
| 219 | # Note: subscriber might promote the AR |
||
| 220 | instance.manage_delObjects(ids=delete_ids) |
||
| 221 | |||
| 222 | # Remove orphaned attachments |
||
| 223 | for attachment in assigned_attachments: |
||
| 224 | # only delete attachments which are no further linked |
||
| 225 | if not attachment.getLinkedAnalyses(): |
||
| 226 | logger.info( |
||
| 227 | "Deleting attachment: {}".format(attachment.getId())) |
||
| 228 | attachment_id = api.get_id(attachment) |
||
| 229 | api.get_parent(attachment).manage_delObjects(attachment_id) |
||
| 230 | |||
| 231 | return new_analyses |
||
| 232 | |||
| 383 |