def getFormattedResult(self, specs=None, decimalmark='.', sciformat=1): """Formatted result: 1. If the result is not floatable, return it without being formatted 2. If the analysis specs has hidemin or hidemax enabled and the result is out of range, render result as '<min' or '>max' 3. If the result is floatable, render it to the correct precision specs param is optional. A dictionary as follows: {'min': <min_val>, 'max': <max_val>, 'error': <error>, 'hidemin': <hidemin_val>, 'hidemax': <hidemax_val>} :param sciformat: 1. The sci notation has to be formatted as aE^+b 2. The sci notation has to be formatted as a·10^b 3. As 2, but with super html entity for exp 4. The sci notation has to be formatted as a·10^b 5. As 4, but with super html entity for exp By default 1 """ result = self.getResult() service = self.getService() # 1. If the result is not floatable, return it without being formatted try: result = float(result) except: return result # 2. If the analysis specs has enabled hidemin or hidemax and the # result is out of range, render result as '<min' or '>max' belowmin = False abovemax = False if not specs: uid = self.getServiceUID() specs = self.aq_parent.getResultsRangeDict().get(uid, {}) hidemin = specs.get('hidemin', '') hidemax = specs.get('hidemax', '') try: belowmin = hidemin and result < float(hidemin) or False except: belowmin = False pass try: abovemax = hidemax and result > float(hidemax) or False except: abovemax = False pass # 2.1. If result is below min and hidemin enabled, return '<min' if belowmin: return formatDecimalMark('< %s' % hidemin, decimalmark) # 2.2. If result is above max and hidemax enabled, return '>max' if abovemax: return formatDecimalMark('> %s' % hidemax, decimalmark) # 3. If the result is floatable, render it to the correct precision return formatDecimalMark(format_numeric_result(self, result, sciformat), decimalmark)
def _analysis_data(self, analysis): """ Returns a dict that represents the analysis """ decimalmark = analysis.aq_parent.aq_parent.getDecimalMark() keyword = analysis.getKeyword() service = analysis.getService() andict = {'obj': analysis, 'id': analysis.id, 'title': analysis.Title(), 'keyword': keyword, 'scientific_name': service.getScientificName(), 'accredited': service.getAccredited(), 'point_of_capture': to_utf8(POINTS_OF_CAPTURE.getValue(service.getPointOfCapture())), 'category': to_utf8(service.getCategoryTitle()), 'result': analysis.getResult(), 'unit': to_utf8(service.getUnit()), 'formatted_unit': format_supsub(to_utf8(service.getUnit())), 'capture_date': analysis.getResultCaptureDate(), 'request_id': analysis.aq_parent.getId(), 'formatted_result': '', 'uncertainty': analysis.getUncertainty(), 'formatted_uncertainty': '', 'retested': analysis.getRetested(), 'remarks': to_utf8(analysis.getRemarks()), 'resultdm': to_utf8(analysis.getResultDM()), 'outofrange': False, 'type': analysis.portal_type, 'reftype': analysis.getReferenceType() \ if hasattr(analysis, 'getReferenceType') else None, 'worksheet': None, 'specs': {}, 'formatted_specs': ''} andict['refsample'] = analysis.getSample().id \ if analysis.portal_type == 'Analysis' \ else '%s - %s' % (analysis.aq_parent.id, analysis.aq_parent.Title()) # Which analysis specs must be used? # Try first with those defined at AR Publish Specs level if analysis.portal_type == 'ReferenceAnalysis': # The analysis is a Control or Blank. We might use the # reference results instead other specs uid = analysis.getServiceUID() specs = analysis.aq_parent.getResultsRangeDict().get(uid, {}) elif analysis.portal_type == 'DuplicateAnalysis': specs = analysis.getAnalysisSpecs() else: ar = analysis.aq_parent specs = ar.getPublicationSpecification() if not specs or keyword not in specs.getResultsRangeDict(): specs = analysis.getAnalysisSpecs() specs = specs.getResultsRangeDict().get(keyword, {}) \ if specs else {} andict['specs'] = specs scinot = self.context.bika_setup.getScientificNotationReport() andict['formatted_result'] = analysis.getFormattedResult( specs=specs, sciformat=int(scinot), decimalmark=decimalmark) fs = '' if specs.get('min', None) and specs.get('max', None): fs = '%s - %s' % (specs['min'], specs['max']) elif specs.get('min', None): fs = '> %s' % specs['min'] elif specs.get('max', None): fs = '< %s' % specs['max'] andict['formatted_specs'] = formatDecimalMark(fs, decimalmark) andict['formatted_uncertainty'] = format_uncertainty( analysis, analysis.getResult(), decimalmark=decimalmark, sciformat=int(scinot)) # Out of range? if specs: adapters = getAdapters((analysis, ), IResultOutOfRange) bsc = getToolByName(self.context, "bika_setup_catalog") for name, adapter in adapters: ret = adapter(specification=specs) if ret and ret['out_of_range']: andict['outofrange'] = True break return andict
def format_uncertainty(analysis, result, decimalmark='.', sciformat=1): """ Returns the formatted uncertainty according to the analysis, result and decimal mark specified following these rules: If the "Calculate precision from uncertainties" is enabled in the Analysis service, and a) If the the non-decimal number of digits of the result is above the service's ExponentialFormatPrecision, the uncertainty will be formatted in scientific notation. The uncertainty exponential value used will be the same as the one used for the result. The uncertainty will be rounded according to the same precision as the result. Example: Given an Analysis with an uncertainty of 37 for a range of results between 30000 and 40000, with an ExponentialFormatPrecision equal to 4 and a result of 32092, this method will return 0.004E+04 b) If the number of digits of the integer part of the result is below the ExponentialFormatPrecision, the uncertainty will be formatted as decimal notation and the uncertainty will be rounded one position after reaching the last 0 (precision calculated according to the uncertainty value). Example: Given an Analysis with an uncertainty of 0.22 for a range of results between 1 and 10 with an ExponentialFormatPrecision equal to 4 and a result of 5.234, this method will return 0.2 If the "Calculate precision from Uncertainties" is disabled in the analysis service, the same rules described above applies, but the precision used for rounding the uncertainty is not calculated from the uncertainty neither the result. The fixed length precision is used instead. For further details, visit https://jira.bikalabs.com/browse/LIMS-1334 If the result is not floatable or no uncertainty defined, returns an empty string. The default decimal mark '.' will be replaced by the decimalmark specified. :param analysis: the analysis from which the uncertainty, precision and other additional info have to be retrieved :param result: result of the analysis. Used to retrieve and/or calculate the precision and/or uncertainty :param decimalmark: decimal mark to use. By default '.' :param sciformat: 1. The sci notation has to be formatted as aE^+b 2. The sci notation has to be formatted as ax10^b 3. As 2, but with super html entity for exp 4. The sci notation has to be formatted as a·10^b 5. As 4, but with super html entity for exp By default 1 :return: the formatted uncertainty """ try: result = float(result) except ValueError: return "" objres = None try: objres = float(analysis.getResult()) except ValueError: pass service = analysis.getService() uncertainty = None if result == objres: # To avoid problems with DLs uncertainty = analysis.getUncertainty() else: uncertainty = analysis.getUncertainty(result) if uncertainty is None or uncertainty == 0: return "" # Scientific notation? # Get the default precision for scientific notation threshold = service.getExponentialFormatPrecision() # Current result precision is above the threshold? sig_digits = get_significant_digits(result) negative = sig_digits < 0 sign = '-' if negative else '' sig_digits = abs(sig_digits) sci = sig_digits >= threshold and sig_digits > 0 formatted = '' if sci: # Scientific notation # 3.2014E+4 if negative == True: res = float(uncertainty)*(10**sig_digits) else: res = float(uncertainty)/(10**sig_digits) res = float(str("%%.%sf" % (sig_digits-1)) % res) res = int(res) if res.is_integer() else res if sciformat in [2,3,4,5]: if sciformat == 2: # ax10^b or ax10^-b formatted = "%s%s%s%s" % (res,"x10^",sign,sig_digits) elif sciformat == 3: # ax10<super>b</super> or ax10<super>-b</super> formatted = "%s%s%s%s%s" % (res,"x10<sup>",sign,sig_digits,"</sup>") elif sciformat == 4: # ax10^b or ax10^-b formatted = "%s%s%s%s" % (res,"·10^",sign,sig_digits) elif sciformat == 5: # ax10<super>b</super> or ax10<super>-b</super> formatted = "%s%s%s%s%s" % (res,"·10<sup>",sign,sig_digits,"</sup>") else: # Default format: aE^+b sig_digits = "%02d" % sig_digits formatted = "%s%s%s%s" % (res,"e",sign,sig_digits) #formatted = str("%%.%se" % sig_digits) % uncertainty else: # Decimal notation prec = analysis.getPrecision(result) prec = prec if prec else '' formatted = str("%%.%sf" % prec) % uncertainty return formatDecimalMark(formatted, decimalmark)
def _analysis_data(self, analysis, decimalmark=None): keyword = analysis.getKeyword() service = analysis.getService() andict = {'obj': analysis, 'id': analysis.id, 'title': analysis.Title(), 'keyword': keyword, 'scientific_name': service.getScientificName(), 'accredited': service.getAccredited(), 'point_of_capture': to_utf8(POINTS_OF_CAPTURE.getValue(service.getPointOfCapture())), 'category': to_utf8(service.getCategoryTitle()), 'result': analysis.getResult(), 'isnumber': isnumber(analysis.getResult()), 'unit': to_utf8(service.getUnit()), 'formatted_unit': format_supsub(to_utf8(service.getUnit())), 'capture_date': analysis.getResultCaptureDate(), 'request_id': analysis.aq_parent.getId(), 'formatted_result': '', 'uncertainty': analysis.getUncertainty(), 'formatted_uncertainty': '', 'retested': analysis.getRetested(), 'remarks': to_utf8(analysis.getRemarks()), 'resultdm': to_utf8(analysis.getResultDM()), 'outofrange': False, 'type': analysis.portal_type, 'reftype': analysis.getReferenceType() \ if hasattr(analysis, 'getReferenceType') else None, 'worksheet': None, 'specs': {}, 'formatted_specs': ''} if analysis.portal_type == 'DuplicateAnalysis': andict['reftype'] = 'd' ws = analysis.getBackReferences('WorksheetAnalysis') andict['worksheet'] = ws[0].id if ws and len(ws) > 0 else None andict['worksheet_url'] = ws[0].absolute_url if ws and len(ws) > 0 else None andict['refsample'] = analysis.getSample().id \ if analysis.portal_type == 'Analysis' \ else '%s - %s' % (analysis.aq_parent.id, analysis.aq_parent.Title()) # Which analysis specs must be used? # Try first with those defined at AR Publish Specs level if analysis.portal_type == 'ReferenceAnalysis': # The analysis is a Control or Blank. We might use the # reference results instead other specs uid = analysis.getServiceUID() specs = analysis.aq_parent.getResultsRangeDict().get(uid, {}) elif analysis.portal_type == 'DuplicateAnalysis': specs = analysis.getAnalysisSpecs(); else: ar = analysis.aq_parent specs = ar.getPublicationSpecification() if not specs or keyword not in specs.getResultsRangeDict(): specs = analysis.getAnalysisSpecs() specs = specs.getResultsRangeDict().get(keyword, {}) \ if specs else {} andict['specs'] = specs scinot = self.context.bika_setup.getScientificNotationReport() andict['formatted_result'] = analysis.getFormattedResult(specs=specs, sciformat=int(scinot), decimalmark=decimalmark) fs = '' if specs.get('min', None) and specs.get('max', None): fs = '%s - %s' % (specs['min'], specs['max']) elif specs.get('min', None): fs = '> %s' % specs['min'] elif specs.get('max', None): fs = '< %s' % specs['max'] andict['formatted_specs'] = formatDecimalMark(fs, decimalmark) andict['formatted_uncertainty'] = format_uncertainty(analysis, analysis.getResult(), decimalmark=decimalmark, sciformat=int(scinot)) # Out of range? if specs: adapters = getAdapters((analysis, ), IResultOutOfRange) bsc = getToolByName(self.context, "bika_setup_catalog") for name, adapter in adapters: ret = adapter(specification=specs) if ret and ret['out_of_range']: andict['outofrange'] = True break return andict
def format_numeric_result(analysis, result, decimalmark='.', sciformat=1): """ Returns the formatted number part of a results value. This is responsible for deciding the precision, and notation of numeric values in accordance to the uncertainty. If a non-numeric result value is given, the value will be returned unchanged. The following rules apply: If the "Calculate precision from uncertainties" is enabled in the Analysis service, and a) If the non-decimal number of digits of the result is above the service's ExponentialFormatPrecision, the result will be formatted in scientific notation. Example: Given an Analysis with an uncertainty of 37 for a range of results between 30000 and 40000, with an ExponentialFormatPrecision equal to 4 and a result of 32092, this method will return 3.2092E+04 b) If the number of digits of the integer part of the result is below the ExponentialFormatPrecision, the result will be formatted as decimal notation and the resulta will be rounded in accordance to the precision (calculated from the uncertainty) Example: Given an Analysis with an uncertainty of 0.22 for a range of results between 1 and 10 with an ExponentialFormatPrecision equal to 4 and a result of 5.234, this method will return 5.2 If the "Calculate precision from Uncertainties" is disabled in the analysis service, the same rules described above applies, but the precision used for rounding the result is not calculated from the uncertainty. The fixed length precision is used instead. For further details, visit https://jira.bikalabs.com/browse/LIMS-1334 The default decimal mark '.' will be replaced by the decimalmark specified. :param analysis: the analysis from which the uncertainty, precision and other additional info have to be retrieved :param result: result to be formatted. :param decimalmark: decimal mark to use. By default '.' :param sciformat: 1. The sci notation has to be formatted as aE^+b 2. The sci notation has to be formatted as ax10^b 3. As 2, but with super html entity for exp 4. The sci notation has to be formatted as a·10^b 5. As 4, but with super html entity for exp By default 1 :return: the formatted result """ try: result = float(result) except ValueError: return result service = analysis.getService() # Scientific notation? # Get the default precision for scientific notation threshold = service.getExponentialFormatPrecision() # Current result precision is above the threshold? sig_digits = get_significant_digits(result) negative = sig_digits < 0 sign = '-' if negative else '' sig_digits = abs(sig_digits) sci = sig_digits >= threshold formatted = '' if sci: # Scientific notation if sciformat in [2,3,4,5]: if negative == True: res = float(result)*(10**sig_digits) else: res = float(result)/(10**sig_digits) res = float(str("%%.%sf" % (sig_digits-1)) % res) res = int(res) if res.is_integer() else res if sciformat == 2: # ax10^b or ax10^-b formatted = "%s%s%s%s" % (res,"x10^",sign,sig_digits) elif sciformat == 3: # ax10<super>b</super> or ax10<super>-b</super> formatted = "%s%s%s%s%s" % (res,"x10<sup>",sign,sig_digits,"</sup>") elif sciformat == 4: # ax10^b or ax10^-b formatted = "%s%s%s%s" % (res,"·10^",sign,sig_digits) elif sciformat == 5: # ax10<super>b</super> or ax10<super>-b</super> formatted = "%s%s%s%s%s" % (res,"·10<sup>",sign,sig_digits,"</sup>") else: # Default format: aE^+b formatted = str("%%.%se" % sig_digits) % result else: # Decimal notation prec = analysis.getPrecision(result) prec = prec if prec else '' formatted = str("%%.%sf" % prec) % result formatted = str(int(float(formatted))) if float(formatted).is_integer() else formatted return formatDecimalMark(formatted, decimalmark)