<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ecnet/tools/database.py
# v.3.2.1
# Developed in 2019 by <NAME> <<EMAIL>>
#
# Contains functions for creating ECNet-formatted databases
#

# Stdlib imports
from csv import writer
from datetime import datetime
from os import remove
from warnings import warn

# 3rd party imports
from alvadescpy import smiles_to_descriptors
from padelpy import from_smiles


class _Molecule:

    def __init__(self, id):

        self.id = id
        self.assignment = 'L'
        self.strings = {'Compound Name': ''}
        self.target = 0
        self.inputs = None


def create_db(smiles: list, db_name: str, targets: list=None,
              id_prefix: str='', extra_strings: dict={}, backend: str='padel'):
    ''' create_db: creates an ECNet-formatted database from SMILES strings
    using either PaDEL-Descriptor or alvaDesc software; using alvaDesc
    requires a valid installation/license of alvaDesc

    Args:
        smiles (list): list of SMILES strings
        db_name (str): name/path of database being created
        targets (list): target (experimental) values, align with SMILES
            strings; if None, all TARGETs set to 0
        id_prefix (str): prefix of molecule DATAID, if desired
        extra_strings (dict): extra STRING columns, label = name, value = list
            with length equal to number of SMILES strings
        backend (str): software used to calculate QSPR descriptors, 'padel' or
            'alvadesc'
    '''

    if targets is not None:
        if len(targets) != len(smiles):
            raise ValueError('Must supply same number of targets as SMILES '
                             'strings: {}, {}'.format(
                                 len(targets), len(smiles)
                             ))

    for string in list(extra_strings.keys()):
        if len(extra_strings[string]) != len(smiles):
            raise ValueError('Extra string values for {} not equal in length '
                             'to supplied SMILES: {}, {}'.format(
                                 len(extra_strings[string]), len(smiles)
                             ))

    mols = []
    if backend == 'alvadesc':
        for mol in smiles:
            mols.append(smiles_to_descriptors(mol))
    elif backend == 'padel':
        for idx, mol in enumerate(smiles):
            try:
                mols.append(from_smiles(mol))
            except RuntimeError:
                warn('Could not calculate descriptors for {}, omitting'.format(
                     mol), RuntimeWarning)
                del smiles[idx]
                if targets is not None:
                    del targets[idx]
                for string in list(extra_strings.keys()):
                    del extra_strings[string][idx]
    else:
        raise ValueError('Unknown backend software: {}'.format(backend))

    rows = []
    type_row = ['DATAID', 'ASSIGNMENT', 'STRING', 'STRING']
    title_row = ['DATAID', 'ASSIGNMENT', 'Compound Name', 'SMILES']
    strings = list(extra_strings.keys())<commit_msg>Added option to convert SMILES to MDL before descriptor generation<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ecnet/tools/database.py
# v.3.2.1
# Developed in 2019 by <NAME> <<EMAIL>>
#
# Contains functions for creating ECNet-formatted databases
#

# Stdlib imports
from csv import writer
from datetime import datetime
from os import remove
from warnings import warn

# 3rd party imports
from alvadescpy import smiles_to_descriptors
from padelpy import from_mdl, from_smiles

try:
    import pybel
except:
    pybel = None


class _Molecule:

    def __init__(self, id):

        self.id = id
        self.assignment = 'L'
        self.strings = {'Compound Name': ''}
        self.target = 0
        self.inputs = None


def create_db(smiles: list, db_name: str, targets: list=None,
              id_prefix: str='', extra_strings: dict={}, backend: str='padel',
              convert_mdl: bool=False):
    ''' create_db: creates an ECNet-formatted database from SMILES strings
    using either PaDEL-Descriptor or alvaDesc software; using alvaDesc
    requires a valid installation/license of alvaDesc

    Args:
        smiles (list): list of SMILES strings
        db_name (str): name/path of database being created
        targets (list): target (experimental) values, align with SMILES
            strings; if None, all TARGETs set to 0
        id_prefix (str): prefix of molecule DATAID, if desired
        extra_strings (dict): extra STRING columns, label = name, value = list
            with length equal to number of SMILES strings
        backend (str): software used to calculate QSPR descriptors, 'padel' or
            'alvadesc'
        convert_mdl (bool): if `True`, converts SMILES strings to MDL 3D
            format before calculating descriptors (PaDEL only)
    '''

    if targets is not None:
        if len(targets) != len(smiles):
            raise ValueError('Must supply same number of targets as SMILES '
                             'strings: {}, {}'.format(
                                 len(targets), len(smiles)
                             ))

    for string in list(extra_strings.keys()):
        if len(extra_strings[string]) != len(smiles):
            raise ValueError('Extra string values for {} not equal in length '
                             'to supplied SMILES: {}, {}'.format(
                                 len(extra_strings[string]), len(smiles)
                             ))

    mols = []
    if backend == 'alvadesc':
        for mol in smiles:
            mols.append(smiles_to_descriptors(mol))
    elif backend == 'padel':
        for idx, mol in enumerate(smiles):
            if convert_mdl is True:
                if pybel is None:
                    raise ImportError(
                        'pybel (Python Open Babel wrapper) not installed, '
                        'cannot convert SMILES to MDL'
                    )
                mdl = pybel.readstring('smi', mol)
                mdl.make3D()
                curr_time = datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3]
                mdl.write('mdl', '{}.mdl'.format(curr_time))
                try:
                    mols.append(from_mdl('{}.mdl'.format(curr_time))[0])
                except RuntimeError:
                    warn('Could not calculate descriptors for {}, omitting'
                         .format(mol), RuntimeWarning)
                    del smiles[idx]
                    if targets is not None:
                        del targets[idx]
                    for string in list(extra_strings.keys()):
                        del extra_strings[string][idx]
                remove('{}.mdl'.format(curr_time))
            else:
                try:
                    mols.append(from_smiles(mol))
                except RuntimeError:
                    warn('Could not calculate descriptors for {}, omitting'
                         .format(mol), RuntimeWarning)
                    del smiles[idx]
                    if targets is not None:
                        del targets[idx]
                    for string in list(extra_strings.keys()):
                        del extra_strings[string][idx]
    else:
        raise ValueError('Unknown backend software: {}'.format(backend))

    rows = []
    type_row = ['DATAID', 'ASSIGNMENT', 'STRING', 'STRING']
    title_row = ['DATAID', 'ASSIGNMENT', 'Compound Name', 'SMILES']
    strings = list(extra_strings.keys())
{'fraction_non_alphanumeric': 0.09761301506867583, 'fraction_numerical': 0.003467128950526737, 'mean_word_length': 1.912621359223301, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4517665', 'n_tokens_mistral': 2080, 'n_tokens_neox': 1983, 'n_words': 635}