def screener(molecules, ensemble, sort_order):
    """
    Uses the virtual screening scores for the receptors, or queries, specified in ensemble to sort the molecules in
    molecules in the direction specified by sort_order.
    :param molecules: a list of molecule objects (/classification/molecules.Molecules())
    :param ensemble: a tuple with receptors, or a query, that specifies a ensemble
    :param sort_order: 'asc' or 'dsc'. 'asc' sorts in ascending order (binding energy estimates) 'dsc' sorts in
    descending order (similarity scores, or binding probabilities)
    :return:
    """

    # screen
    modified_molecules = []
    for index in range(len(molecules)):
        modified_molecules.append(molecules[index])
        scores = []     #[(score, query)]

        for query in ensemble:
            scores.append( (molecules[index].GetProp(query), query))

        if sort_order == 'dsc':
            scores.sort(key=lambda x: float(x[0]), reverse=True)

        elif sort_order == 'asc':
            scores.sort(key=lambda x: float(x[0]))

        modified_molecules[index].SetProp('best_score', format(scores[0][0]))
        modified_molecules[index].SetProp('best_query', format(scores[0][1]))

    active = []
    decoy = []
    non_random = []
    for mol in modified_molecules:
        if float(mol.GetProp('best_score')) == 10000.00:
            if mol.GetProp('status') == 1:
                active.append(mol)
            else:
                decoy.append(mol)
        else:
            non_random.append(mol)

    if sort_order == 'dsc':
        non_random.sort(key=lambda mol: float(mol.GetProp('best_score')), reverse=True)
        #random.shuffle(rand)
    elif sort_order == 'asc':
        non_random.sort(key=lambda mol: float(mol.GetProp('best_score')))
        #random.shuffle(rand)
    # append the compounds with scores of 10,000 in the order active, decoy, active, ...
    rand = []
    decoy_length = len(decoy)
    active_length = len(active)
    if decoy_length > active_length:
        for a, d in zip(active, decoy[0:active_length]):
            rand.append(a)
            rand.append(d)
        for d in decoy[active_length:decoy_length]:
            rand.append(d)
    elif decoy_length < active_length:
        for a, d in zip(active[0:decoy_length], decoy):
            rand.append(a)
            rand.append(d)
        for a in active[decoy_length:active_length]:
            rand.append(a)
    elif decoy_length == active_length:
        for a, d in zip(active, decoy):
            rand.append(a)
            rand.append(d)

    modified_molecules = non_random + rand

    return modified_molecules