widget=RecordsWidget( label=_("Formatting Configuration"), allowDelete=True, description=_( " <p>The Bika LIMS ID Server provides unique sequential IDs " "for objects such as Samples and Worksheets etc, based on a " "format specified for each content type.</p>" "<p>The format is constructed similarly to the Python format" " syntax, using predefined variables per content type, and" " advancing the IDs through a sequence number, 'seq' and its" " padding as a number of digits, e.g. '03d' for a sequence of" " IDs from 001 to 999.</p>" "<p>Alphanumeric prefixes for IDs are included as is in the" " formats, e.g. WS for Worksheet in WS-{seq:03d} produces" " sequential Worksheet IDs: WS-001, WS-002, WS-003 etc.</p>" "<p>Variables that can be used include:" "<table>" "<tr>" "<th style='width:150px'>Content Type</th><th>Variables</th>" "</tr>" "<tr><td>Client</td><td>{client}</td></tr>" "<tr><td>Year</td><td>{year}</td></tr>" "<tr><td>Sample ID</td><td>{sampleId}</td></tr>" "<tr><td>Sample Type</td><td>{sampleType}</td></tr>" "<tr><td>Sampling Date</td><td>{samplingDate}</td></tr>" "<tr><td>Date Sampled</td><td>{dateSampled}</td></tr>" "</table>" "</p>" "<p>Configuration Settings:" "<ul>" "<li>format:" "<ul><li>a python format string constructed from predefined" " variables like sampleId, client, sampleType.</li>" "<li>special variable 'seq' must be positioned last in the" "format string</li></ul></li>" "<li>sequence type: [generated|counter]</li>" "<li>context: if type counter, provides context the counting" " function</li>" "<li>counter type: [backreference|contained]</li>" "<li>counter reference: a parameter to the counting" " function</li>" "<li>prefix: default prefix if none provided in format" " string</li>" "<li>split length: the number of parts to be included in the" " prefix</li>" "</ul></p>"))),
Calculation = HistoryAwareReferenceField('Calculation', allowed_types=('Calculation', ), relationship='AnalysisCalculation', referenceClass=HoldingReference) # InterimFields are defined in Calculations, Services, and Analyses. # In Analysis Services, the default values are taken from Calculation. # In Analyses, the default values are taken from the Analysis Service. # When instrument results are imported, the values in analysis are overridden # before the calculation is performed. InterimFields = InterimFieldsField( 'InterimFields', schemata='Method', widget=RecordsWidget( label=_("Calculation Interim Fields"), description=_( "Values can be entered here which will override the defaults " "specified in the Calculation Interim Fields."), )) schema = schema.copy() + Schema(( AnalysisService, Analyst, Attachment, DetectionLimitOperand, # NumberOfRequiredVerifications overrides AbstractBaseClass NumberOfRequiredVerifications, Result, ResultCaptureDate, RetestOf, Uncertainty, Calculation,
), #TODO: To be removed? RecordsField( 'DataInterfaceOptions', type='interfaceoptions', subfields=('Key', 'Value'), required_subfields=('Key', 'Value'), subfield_labels={ 'OptionValue': _('Key'), 'OptionText': _('Value'), }, widget=RecordsWidget( label=_("Data Interface Options"), description= _("Use this field to pass arbitrary parameters to the export/import modules." ), visible=False, ), ), # References to all analyses performed with this instrument. # Includes regular analyses, QC analyes and Calibration tests. ReferenceField( 'Analyses', required=0, multiValued=1, allowed_types=('ReferenceAnalysis', 'DuplicateAnalysis', 'Analysis'), relationship='InstrumentAnalyses', widget=ReferenceWidget(visible=False, ), ),
required_subfields=('intercept_min', 'intercept_max', 'errorvalue'), subfield_sizes={ 'intercept_min': 10, 'intercept_max': 10, 'errorvalue': 10, }, subfield_labels={ 'intercept_min': _('Range min'), 'intercept_max': _('Range max'), 'errorvalue': _('Uncertainty value'), }, widget=RecordsWidget( label=_("Uncertainty"), description= _("Specify the uncertainty value for a given range, e.g. for results " "in a range with minimum of 0 and maximum of 10, the uncertainty " "value is 0.5 - a result of 6.67 will be reported as 6.67 +- 0.5. " "Please ensure successive ranges are continuous, e.g. 0.00 - 10.00 " "is followed by 10.01 - 20.00, 20.01 - 30 .00 etc."), ), ), RecordsField( 'ResultOptions', schemata=PMF("Result Options"), type='resultsoptions', subfields=('ResultValue', 'ResultText'), required_subfields=('ResultValue', 'ResultText'), subfield_labels={ 'ResultValue': _('Result Value'), 'ResultText': _('Display Value'), },
class BikaSetupSchemaExtender(object): adapts(IBikaSetup) implements(ISchemaExtender) fields = [ ExtFixedPointField( 'LevyAmount', schemata='Accounting', default='0.00', widget=DecimalWidget( label=_("Levy Amount"), description= _("The levy percentage the university or parent organisation raises on all invoiced amounts" ), )), ExtRecordsField( 'StoragePricing', schemata='Storage', subfields=('storage_type', 'price', 'storage_type_uid'), subfield_hidden={'storage_type_uid': True}, required_subfields=('storage_type', 'price', 'storage_type_uid'), subfield_sizes={ 'storage_type': 50, 'price': 5 }, subfield_labels={ 'storage_type': _('Storage Type'), 'price': _('Price') }, widget=RecordsWidget( label=_("Storage Pricing"), description=_( "Set Sample storage pricing depending on storage type."), allowDelete=False, combogrid_options={ 'storage_type': { 'colModel': [{ 'columnName': 'storage_type', 'width': '30', 'label': _('Title') }, { 'columnName': 'Description', 'width': '70', 'label': _('Description') }, { 'columnName': 'storage_type_uid', 'hidden': True }], 'url': 'getstoragetypes', 'showOn': True, 'width': '550px' }, }, )), ExtBooleanField( 'ShowPartitions', schemata="Analyses", default=False, widget=BooleanWidget( label=_("Display individual sample partitions "), description=_( "Turn this on if you want to work with sample partitions") ), ), ExtBooleanField( 'StoreKitBiospecimens', schemata="Storage", default=True, widget=BooleanWidget( label=_("Store biospecimens when kit creation"), description= _("Turn this off if you want create kits without storing its biospecimens in managed storages" )), ), ] def __init__(self, context): self.context = context def getFields(self): return self.fields
subfield_readonly={'module': False, 'function': False}, subfield_types={'module': 'string', 'function': 'string'}, default=[ {'module': 'math', 'function': 'ceil'}, {'module': 'math', 'function': 'floor'}, ], subfield_validators={ 'module': 'importvalidator', }, widget=RecordsWidget( label=_("Additional Python Libraries"), description=_( "If your formula needs a special function from an external " "Python library, you can import it here. E.g. if you want to " "use the 'floor' function from the Python 'math' module, " "you add 'math' to the Module field and 'floor' to the " "function field. The equivalent in Python would be 'from math " "import floor'. In your calculation you could use then " "'floor([Ca] + [Mg])'. " ), allowDelete=True, ), ), TextField( 'Formula', validators=('formulavalidator',), default_content_type='text/plain', allowable_content_types=('text/plain',), widget=TextAreaWidget( label=_("Calculation Formula"),
'ResultFilesFolder', subfields=('InterfaceName', 'Folder'), subfield_labels={ 'InterfaceName': _('Interface Code'), 'Folder': _('Folder that results will be saved') }, subfield_readonly={ 'InterfaceName': True, 'Folder': False }, widget=RecordsWidget( label=_("Result files folders"), description=_("For each interface of this instrument, \ you can define a folder where \ the system should look for the results files while \ automatically importing results. Having a folder \ for each Instrument and inside that folder creating \ different folders for each of its Interfaces \ can be a good approach. You can use Interface codes \ to be sure that folder names are unique."), visible=True, ), ), RecordsField( 'DataInterfaceOptions', type='interfaceoptions', subfields=('Key', 'Value'), required_subfields=('Key', 'Value'), subfield_labels={ 'OptionValue': _('Key'), 'OptionText': _('Value'), },
'prefix': 'WS', 'padding': '4' }, { 'portal_type': 'Pricelist', 'prefix': 'PL', 'padding': '4' }, ], # fixedSize=8, widget=RecordsWidget( label=_("Prefixes"), description= _("Define the prefixes for the unique sequential IDs the system issues for " "objects. In the 'Padding' field, indicate with how many leading zeros the " "numbers must be padded. E.g. a prefix of WS for worksheets with padding of " "4, will see them numbered from WS-0001 to WS-9999. NB: Note that samples " "and analysis requests are prefixed with sample type abbreviations and are " "not configured in this table - their padding can be set in the specified " "fields below"), allowDelete=False, )), BooleanField( 'YearInPrefix', schemata="ID Server", default=False, widget=BooleanWidget( label=_("Include year in ID prefix"), description=_("Adds a two-digit year after the ID prefix")), ), IntegerField( 'SampleIDPadding',
'portal_type': 'Project', 'prefix': 'SU', 'padding': '4', 'separator': '-', 'sequence_start': '0' }, ], # fixedSize=8, widget=RecordsWidget( label=_("Prefixes"), description= _("Define the prefixes for the unique sequential IDs the system issues for " "objects. In the 'Padding' field, indicate with how many leading zeros the " "numbers must be padded. E.g. a prefix of WS for worksheets with padding of " "4, will see them numbered from WS-0001 to WS-9999. Sequence Start " "indicates the number from which the next ID should start. This is " "set only if it is greater than existing id numbers. Note that the " "gap created by jumping IDs cannot be refilled. NB: Note that samples " "and analysis requests are prefixed with sample type abbreviations and are " "not configured in this table - their padding can be set in the specified " "fields below"), allowDelete=False, )), BooleanField( 'YearInPrefix', schemata="ID Server", default=False, widget=BooleanWidget( label=_("Include year in ID prefix"), description=_("Adds a two-digit year after the ID prefix")), ),
schema = BikaSchema.copy() + Schema((RecordsField( 'AetiologicAgentSubtypes', type='aetiologicagentsubtypes', subfields=('Subtype', 'SubtypeRemarks'), subfield_labels={ 'Subtype': _('Subtype'), 'SubtypeRemarks': _('Remarks') }, subfield_sizes={ 'Subtype': 10, 'SubtypeRemarks': 25 }, widget=RecordsWidget( label='Subtypes', description=_("A list of aetiologic agent subtypes."), visible=True, ), ), )) schema['description'].widget.visible = True schema['description'].schemata = 'default' class AetiologicAgent(BaseContent): security = ClassSecurityInfo() displayContentsTab = False schema = schema _at_rename_after_creation = True def _renameAfterCreation(self, check_auto_id=False):
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False): """ Gets the values from the ReflexRule section and returns them in the correct way to be saved. So the function will return a list of dictionaries: [...,{ 'conditions':[{ 'range1': 'X', 'range0': 'X', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, {...}], 'trigger': 'xxx', 'actions':[{'action':'<action_name>', 'act_row_idx':'X', 'otherWS':'to_another', 'analyst': '<analyst_id>', 'an_result_id':'rep-1',...}, ] }, { 'conditions':[{ 'range1': 'X', 'range0': 'X', 'trigger': 'xxx', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, { 'discreteresult': 'X', 'trigger': 'xxx', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, {...}], 'trigger': 'xxx', 'actions':[{'action':'<action_name>', 'act_row_idx':'X', 'otherWS':to_another, 'analyst': '<analyst_id>', 'an_result_id':'rep-1',...}, {'action':'<action_name>', 'act_row_idx':'X', 'otherWS':to_another, 'analyst': '<analyst_id>', 'an_result_id':'rep-2'...}, ] }, ...] """ if field.getName() != 'ReflexRules': return RecordsWidget.process_form(self, instance, field, form, empty_marker, emptyReturnsMarker) raw_data = RecordsWidget.process_form(self, instance, field, form, empty_marker, emptyReturnsMarker) # 'value' is a list which will be saved value = [] rulenum = 0 # Building the actions and conditions list for raw_set in raw_data[0]: d = self._format_conditions_and_actions(raw_set) # Adding the rule number d['rulenumber'] = str(rulenum) # Filling the dict with the mother service UID d['mother_service_uid'] = raw_data[0][0].get( 'analysisservice-0', '') value.append(d) rulenum += 1 return value, {}
subfield_sizes={ 'Identifier': 15, 'Identifier Type': 25 }, widget=RecordsWidget( label=_('Additional identifiers'), description=_('Patient additional identifiers'), combogrid_options={ 'IdentifierType': { 'colModel': [ { 'columnName': 'IdentifierType', 'width': '30', 'label': _('Title') }, { 'columnName': 'Description', 'width': '70', 'label': _('Description') } ], 'url': 'getidentifiertypes', 'showOn': True, 'width': '550px' }, }, ), ), ComputedField( 'PatientIdentifiersStr', expression="context.getPatientIdentifiersStr()",
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False): """ Gets the values from the ReflexRule section and returns them in the correct way to be saved. So the function will return a list of dictionaries: [...,{ 'conditions':[{ 'range1': 'X', 'range0': 'X', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, {...}], 'trigger': 'xxx', 'actions':[{'action':'<action_name>', 'act_row_idx':'X', 'otherWS':'to_another', 'analyst': '<analyst_id>', 'an_result_id':'rep-1',...}, ] }, { 'conditions':[{ 'range1': 'X', 'range0': 'X', 'trigger': 'xxx', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, { 'discreteresult': 'X', 'trigger': 'xxx', 'cond_row_idx':'X' 'and_or': 'and', 'analysisservice': '<as_uid>', }, {...}], 'trigger': 'xxx', 'actions':[{'action':'<action_name>', 'act_row_idx':'X', 'otherWS':to_another, 'analyst': '<analyst_id>', 'an_result_id':'rep-1',...}, {'action':'<action_name>', 'act_row_idx':'X', 'otherWS':to_another, 'analyst': '<analyst_id>', 'an_result_id':'rep-2'...}, ] }, ...] """ if field.getName() != 'ReflexRules': return RecordsWidget.process_form( self, instance, field, form, empty_marker, emptyReturnsMarker) raw_data = RecordsWidget.process_form( self, instance, field, form, empty_marker, emptyReturnsMarker) # 'value' is a list which will be saved value = [] rulenum = 0 # Building the actions and conditions list for raw_set in raw_data[0]: d = self._format_conditions_and_actions(raw_set) # Adding the rule number d['rulenumber'] = str(rulenum) # Filling the dict with the mother service UID d['mother_service_uid'] = raw_data[0][0].get( 'analysisservice-0', '') value.append(d) rulenum += 1 return value, {}
}, widget=RecordsWidget( label=_('Identifiers for this object'), description=_( "Select identifiers by which this object can be referenced."), visible={ 'view': 'visible', 'edit': 'visible' }, combogrid_options={ 'IdentifierType': { 'colModel': [{ 'columnName': 'identifiertype_uid', 'hidden': True }, { 'columnName': 'IdentifierType', 'width': '30', 'label': _('Identifier Type') }, { 'columnName': 'Description', 'width': '70', 'label': _('Description') }], 'url': 'getidentifiertypes', 'showOn': True, 'width': '500px', } }))