def test_set_solute_moles_should_fail(self): solute = message_helpers.build_compound(name='Solute') solvent = message_helpers.build_compound(name='Solvent') with self.assertRaisesRegex(ValueError, 'defined volume'): message_helpers.set_solute_moles(solute, [solvent], '10 mM') solute = message_helpers.build_compound(name='Solute', amount='1 mol') solvent = message_helpers.build_compound(name='Solvent', amount='1 L') with self.assertRaisesRegex(ValueError, 'overwrite'): message_helpers.set_solute_moles(solute, [solvent], '10 mM')
def test_set_solute_moles(self): solute = message_helpers.build_compound(name='Solute') solvent2 = message_helpers.build_compound(name='Solvent', amount='100 mL') message_helpers.set_solute_moles(solute, [solvent2], '1 molar') self.assertEqual(solute.amount.moles, reaction_pb2.Moles(units='MILLIMOLE', value=100)) solvent3 = message_helpers.build_compound(name='Solvent', amount='75 uL') message_helpers.set_solute_moles(solute, [solvent3], '3 mM', overwrite=True) self.assertEqual(solute.amount.moles, reaction_pb2.Moles(units='NANOMOLE', value=225)) solvent4 = message_helpers.build_compound(name='Solvent', amount='0.2 uL') message_helpers.set_solute_moles(solute, [solvent4], '30 mM', overwrite=True) self.assertEqual(solute.amount.moles, reaction_pb2.Moles(units='NANOMOLE', value=6)) solvent5 = message_helpers.build_compound(name='Solvent', amount='0.8 uL') message_helpers.set_solute_moles(solute, [solvent4, solvent5], '30 mM', overwrite=True) self.assertEqual(solute.amount.moles, reaction_pb2.Moles(units='NANOMOLE', value=30))
def resolve_input(input_string): """Resolve a text-based description of an input in one of the following formats: (1) [AMOUNT] of [NAME] (2) [AMOUNT] of [CONCENTRATION] [SOLUTE] in [SOLVENT] Args: input_string: String describing the input. Returns: ReactionInput message. Raises: ValueError: if the string cannot be parsed properly. """ reaction_input = reaction_pb2.ReactionInput() if ' of ' not in input_string: raise ValueError('String does not match template!') amount_string, description = input_string.split(' of ') if ' in ' not in description: component_name = description component = reaction_input.components.add() component.CopyFrom( message_helpers.build_compound(name=component_name.strip(), amount=amount_string)) resolve_names(reaction_input) return reaction_input pattern = re.compile(r'(\d+.?\d*)\s?(\w+)\s(.+)\sin\s(.+)') match = pattern.fullmatch(description.strip()) if not match: raise ValueError('String did not match template!') conc_value, conc_units, solute_name, solvent_name = match.groups() solute = reaction_input.components.add() solvent = reaction_input.components.add() solute.CopyFrom(message_helpers.build_compound(name=solute_name.strip())) solvent.CopyFrom( message_helpers.build_compound(name=solvent_name.strip(), amount=amount_string)) if solvent.amount.WhichOneof('kind') != 'volume': raise ValueError('Total amount of solution must be a volume!') solvent.amount.volume_includes_solutes = True message_helpers.set_solute_moles(solute, [solvent], f'{conc_value} {conc_units}') resolve_names(reaction_input) return reaction_input