Returns FASTA entries for subject sequences from BLAST hits

    Stores B6/M8 entries with E-Values below the e_value cutoff. Then iterates
    through the FASTA file and if an entry matches the query of an B6/M8
    entry, it's sequence is extracted and returned as a FASTA entry
    plus the E-Value.

    Args:
        fastaq_handle (file): FASTA or FASTQ file handle, can technically
            be any iterable that returns FASTA/Q "lines"

        b6_handle (file): B6/M8 file handle, can technically
            be any iterable that returns B6/M8"lines"

        e_value (float): Max E-Value of entry to return

        fastaq (str): ['fasta', 'fastq'] whether file handle is a FASTA or
            FASTQ file

        *args: Variable length argument list for b6_iter

        **kwargs: Arbitrary keyword arguments for b6_iter

    Yields:
        FastaEntry: class containing all FASTA data
            FastqEntry if fastaq='fastq'

    Example:
        Note: These doctests will not pass, examples are only in doctest
        format as per convention. bio_utils uses pytests for testing.

        >>> fasta_handle = open('test.fasta')
        >>> b6_handle = open('test.b6')
        >>> for entry in query_sequence_retriever(fasta_handle,
        ...                                       b6_handle, 1e5)
        ...     print(entry.sequence)  # Print aligned query sequence

def query_sequence_retriever(fastaq_handle, b6_handle, e_value,
                             fastaq='fasta', *args, **kwargs):
    """Returns FASTA entries for subject sequences from BLAST hits

    Stores B6/M8 entries with E-Values below the e_value cutoff. Then iterates
    through the FASTA file and if an entry matches the query of an B6/M8
    entry, it's sequence is extracted and returned as a FASTA entry
    plus the E-Value.

    Args:
        fastaq_handle (file): FASTA or FASTQ file handle, can technically
            be any iterable that returns FASTA/Q "lines"

        b6_handle (file): B6/M8 file handle, can technically
            be any iterable that returns B6/M8"lines"

        e_value (float): Max E-Value of entry to return

        fastaq (str): ['fasta', 'fastq'] whether file handle is a FASTA or
            FASTQ file

        *args: Variable length argument list for b6_iter

        **kwargs: Arbitrary keyword arguments for b6_iter

    Yields:
        FastaEntry: class containing all FASTA data
            FastqEntry if fastaq='fastq'

    Example:
        Note: These doctests will not pass, examples are only in doctest
        format as per convention. bio_utils uses pytests for testing.

        >>> fasta_handle = open('test.fasta')
        >>> b6_handle = open('test.b6')
        >>> for entry in query_sequence_retriever(fasta_handle,
        ...                                       b6_handle, 1e5)
        ...     print(entry.sequence)  # Print aligned query sequence
    """

    filtered_b6 = defaultdict(list)
    for entry in b6_evalue_filter(b6_handle, e_value, *args, **kwargs):
        filtered_b6[entry.query].append(
            (entry.query_start, entry.query_end, entry._evalue_str))
    fastaq_iter = fasta_iter if fastaq == 'fasta' else fastq_iter
    for fastaqEntry in fastaq_iter(fastaq_handle):
        if fastaqEntry.id in filtered_b6:
            for alignment in filtered_b6[fastaqEntry.id]:
                start = alignment[0] - 1
                end = alignment[1] - 1

                # Get query sequence
                if start < end:
                    query_sequence = fastaqEntry.sequence[start:end]
                elif start > end:
                    query_sequence = fastaqEntry.sequence[end:start][::-1]
                else:
                    query_sequence = fastaqEntry.sequence[start]
                fastaqEntry.sequence = query_sequence

                # Get query quality
                if fastaq == 'fastq':
                    if start < end:
                        query_quality = fastaqEntry.quality[start:end]
                    elif start > end:
                        query_quality = fastaqEntry.quality[end:start][::-1]
                    else:
                        query_quality = fastaqEntry.quality[start]
                    fastaqEntry.quality = query_quality

                # Add E-value to FASTA/Q header
                if fastaqEntry.description == '':
                    fastaqEntry.description = 'E-value: '
                else:
                    fastaqEntry.description += ' E-value: '
                fastaqEntry.description += alignment[2]

                yield fastaqEntry