def test_identifier_getters(self): compound = reaction_pb2.Compound() compound.identifiers.add(type='NAME', value='water') self.assertEqual(message_helpers.get_compound_name(compound), 'water') self.assertIsNone(message_helpers.get_compound_smiles(compound)) compound.identifiers.add(type='SMILES', value='O') self.assertEqual(message_helpers.get_compound_smiles(compound), 'O') self.assertEqual(message_helpers.smiles_from_compound(compound), 'O') compound = reaction_pb2.Compound() compound.identifiers.add(type='MOLBLOCK', value=_BENZENE_MOLBLOCK) self.assertEqual(message_helpers.get_compound_molblock(compound), _BENZENE_MOLBLOCK) self.assertEqual(message_helpers.molblock_from_compound(compound), _BENZENE_MOLBLOCK)
def test_render_compound(client): compound = reaction_pb2.Compound() compound.identifiers.add(value="c1ccccc1", type="SMILES") response = client.post("/render/reaction", data=compound.SerializeToString(), follow_redirects=True) assert response.status_code == 200
def test_get_molfile_no_structure(self): compound = reaction_pb2.Compound() compound.identifiers.add(value='benzene', type='NAME') response = self.client.post('/ketcher/molfile', data=compound.SerializeToString(), follow_redirects=True) self.assertEqual(response.status_code, 204)
def test_get_molfile_no_structure(client): compound = reaction_pb2.Compound() compound.identifiers.add(value="benzene", type="NAME") response = client.post("/ketcher/molfile", data=compound.SerializeToString(), follow_redirects=True) assert response.status_code == 204
def test_render_compound(self): compound = reaction_pb2.Compound() compound.identifiers.add(value='c1ccccc1', type='SMILES') response = self.client.post('/render/reaction', data=compound.SerializeToString(), follow_redirects=True) self.assertEqual(response.status_code, 200)
def test_smiles_and_name(self): compound = message_helpers.build_compound(smiles='c1ccccc1', name='benzene') expected = reaction_pb2.Compound(identifiers=[ reaction_pb2.CompoundIdentifier(value='c1ccccc1', type='SMILES'), reaction_pb2.CompoundIdentifier(value='benzene', type='NAME') ]) self.assertEqual(compound, expected)
def get_molfile(): """Retrieves a POSTed Compound message string and returns a MolFile.""" compound = reaction_pb2.Compound() compound.ParseFromString(flask.request.get_data()) try: molblock = message_helpers.molblock_from_compound(compound) return flask.jsonify(molblock) except ValueError: return 'no existing structural identifier', 204
def render_compound(): """Returns an HTML-tagged SVG for the given Compound.""" compound = reaction_pb2.Compound() compound.ParseFromString(flask.request.get_data()) try: mol = message_helpers.mol_from_compound(compound) return flask.jsonify(drawing.mol_to_svg(mol)) except ValueError: return ''
def test_mol_from_compound(self, value, identifier_type, expected): compound = reaction_pb2.Compound() compound.identifiers.add(value=value, type=identifier_type) mol = message_helpers.mol_from_compound(compound) self.assertEqual(Chem.MolToSmiles(mol), expected) mol, identifier = message_helpers.mol_from_compound( compound, return_identifier=True) self.assertEqual(Chem.MolToSmiles(mol), expected) self.assertEqual(identifier, compound.identifiers[0])
def test_get_molfile(self): smiles = 'c1ccccc1' compound = reaction_pb2.Compound() compound.identifiers.add(value=smiles, type='SMILES') response = self.client.post('/ketcher/molfile', data=compound.SerializeToString(), follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertEqual(json.loads(response.data), Chem.MolToMolBlock(Chem.MolFromSmiles(smiles)))
def test_check_compound_identifiers(self): compound = reaction_pb2.Compound() compound.identifiers.add(value='c1ccccc1', type='SMILES') message_helpers.check_compound_identifiers(compound) compound.identifiers.add(value='InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H', type='INCHI') message_helpers.check_compound_identifiers(compound) compound.identifiers.add(value='c1ccc(O)cc1', type='SMILES') with self.assertRaisesRegex(ValueError, 'inconsistent'): message_helpers.check_compound_identifiers(compound)
def test_get_molfile(client): smiles = "c1ccccc1" compound = reaction_pb2.Compound() compound.identifiers.add(value=smiles, type="SMILES") response = client.post("/ketcher/molfile", data=compound.SerializeToString(), follow_redirects=True) assert response.status_code == 200 assert json.loads(response.data) == Chem.MolToMolBlock( Chem.MolFromSmiles(smiles))
def test_compound_rdkit_binary(self): mol = Chem.MolFromSmiles('CC(=O)OC1=CC=CC=C1C(=O)O') message = reaction_pb2.Compound() identifier = message.identifiers.add() identifier.type = identifier.SMILES identifier.value = Chem.MolToSmiles(mol) validations.validate_message(message) # Message is modified in place. self.assertEqual( message.identifiers[1], reaction_pb2.CompoundIdentifier(type='RDKIT_BINARY', bytes_value=mol.ToBinary()))
def test_compound_name_resolver(self): message = reaction_pb2.Compound() identifier = message.identifiers.add() identifier.type = identifier.NAME identifier.value = 'aspirin' validations.validate_message(message) # Message is modified in place. self.assertEqual( message.identifiers[1], reaction_pb2.CompoundIdentifier( type='SMILES', value='CC(=O)OC1=CC=CC=C1C(=O)O', details='NAME resolved by PubChem'))
def test_identifier_setters(self): compound = reaction_pb2.Compound() identifier = message_helpers.set_compound_name(compound, 'water') self.assertEqual( identifier, reaction_pb2.CompoundIdentifier(type='NAME', value='water')) self.assertEqual( compound.identifiers[0], reaction_pb2.CompoundIdentifier(type='NAME', value='water')) message_helpers.set_compound_smiles(compound, 'O') self.assertEqual( compound.identifiers[1], reaction_pb2.CompoundIdentifier(type='SMILES', value='O')) identifier = message_helpers.set_compound_name(compound, 'ice') self.assertEqual( identifier, reaction_pb2.CompoundIdentifier(type='NAME', value='ice')) self.assertEqual( compound.identifiers[0], reaction_pb2.CompoundIdentifier(type='NAME', value='ice')) compound = reaction_pb2.Compound() identifier = message_helpers.set_compound_molblock( compound, _BENZENE_MOLBLOCK) self.assertEqual(_BENZENE_MOLBLOCK, compound.identifiers[0].value)
def test_mol_from_compound_failures(self, value, identifier_type): compound = reaction_pb2.Compound() compound.identifiers.add(value=value, type=identifier_type) with self.assertRaisesRegex(ValueError, 'invalid structural identifier'): message_helpers.mol_from_compound(compound)
def test_smiles_from_compound(self, value, identifier_type, expected): compound = reaction_pb2.Compound() compound.identifiers.add(value=value, type=identifier_type) self.assertEqual(message_helpers.smiles_from_compound(compound), expected)
def build_compound(smiles=None, name=None, amount=None, role=None, is_limiting=None, prep=None, prep_details=None, vendor=None): """Builds a Compound message with the most common fields. Args: smiles: Text compound SMILES. name: Text compound name. amount: Text amount string, e.g. '1.25 g'. role: Text reaction role. Must match a value in ReactionRoleType. is_limiting: Boolean whether this compound is limiting for the reaction. prep: Text compound preparation type. Must match a value in PreparationType. prep_details: Text compound preparation details. If provided, `prep` is required. vendor: Text compound vendor/supplier. Returns: Compound message. Raises: KeyError: if `role` or `prep` does not match a supported enum value. TypeError: if `amount` units are not supported. ValueError: if `prep_details` is provided and `prep` is None. """ compound = reaction_pb2.Compound() if smiles: compound.identifiers.add(value=smiles, type='SMILES') if name: compound.identifiers.add(value=name, type='NAME') if amount: resolver = units.UnitResolver() amount_pb = resolver.resolve(amount) if isinstance(amount_pb, reaction_pb2.Mass): compound.amount.mass.CopyFrom(amount_pb) elif isinstance(amount_pb, reaction_pb2.Moles): compound.amount.moles.CopyFrom(amount_pb) elif isinstance(amount_pb, reaction_pb2.Volume): compound.amount.volume.CopyFrom(amount_pb) else: raise TypeError(f'unsupported units for amount: {amount_pb}') if role: field = reaction_pb2.Compound.DESCRIPTOR.fields_by_name[ 'reaction_role'] values_dict = field.enum_type.values_by_name try: compound.reaction_role = values_dict[role.upper()].number except KeyError as error: raise KeyError( f'{role} is not a supported type: {values_dict.keys()}' ) from error if is_limiting is not None: if not (is_limiting is True or is_limiting is False): raise TypeError( f'is_limiting must be a boolean value: {is_limiting}') compound.is_limiting = is_limiting if prep: field = reaction_pb2.CompoundPreparation.DESCRIPTOR.fields_by_name[ 'type'] values_dict = field.enum_type.values_by_name try: compound.preparations.add().type = values_dict[prep.upper()].number except KeyError as error: raise KeyError( f'{prep} is not a supported type: {values_dict.keys()}' ) from error if (compound.preparations[0].type == reaction_pb2.CompoundPreparation.CUSTOM and not prep_details): raise ValueError( 'prep_details must be provided when CUSTOM prep is used') if prep_details: if not prep: raise ValueError('prep must be provided when prep_details is used') compound.preparations[0].details = prep_details if vendor: compound.source.vendor = vendor return compound