Select a single transcript as a representative for each gene.

    This function is a generalization of the `primary_mrna` function that
    attempts, under certain conditions, to select a single transcript as a
    representative for each gene. If a gene encodes multiple transcript types,
    one of those types must be **mRNA** or the function will complain loudly
    and fail.

    For mRNAs, the primary transcript is selected according to translated
    length. For all other transcript types, the length of the transcript
    feature itself is used. I'd be eager to hear suggestions for alternative
    selection criteria.

    Like the `primary_mrna` function, this function **does not** return only
    transcript features. It **does** modify gene features to ensure that each
    has at most one transcript feature.

    >>> reader = tag.GFF3Reader(tag.pkgdata('psyllid-mixed-gene.gff3.gz'))
    >>> gene_filter = tag.select.features(reader, type='gene')
    >>> trans_filter = tag.transcript.primary_transcript(gene_filter)
    >>> for gene in trans_filter:
    ...     assert gene.num_children == 1

    In cases where the direct children of a gene feature have heterogenous
    types, the `primary_mrna` function will only discard mRNA features. This
    function, however, will discard all direct children of the gene that are
    not the primary transcript, including non-transcript children. This is a
    retty subtle distinction, and anecdotal experience suggests that cases in
    which the distinction actually matters are extremely rare.

def primary_transcript(entrystream, parenttype='gene', logstream=stderr):
    """
    Select a single transcript as a representative for each gene.

    This function is a generalization of the `primary_mrna` function that
    attempts, under certain conditions, to select a single transcript as a
    representative for each gene. If a gene encodes multiple transcript types,
    one of those types must be **mRNA** or the function will complain loudly
    and fail.

    For mRNAs, the primary transcript is selected according to translated
    length. For all other transcript types, the length of the transcript
    feature itself is used. I'd be eager to hear suggestions for alternative
    selection criteria.

    Like the `primary_mrna` function, this function **does not** return only
    transcript features. It **does** modify gene features to ensure that each
    has at most one transcript feature.

    >>> reader = tag.GFF3Reader(tag.pkgdata('psyllid-mixed-gene.gff3.gz'))
    >>> gene_filter = tag.select.features(reader, type='gene')
    >>> trans_filter = tag.transcript.primary_transcript(gene_filter)
    >>> for gene in trans_filter:
    ...     assert gene.num_children == 1

    In cases where the direct children of a gene feature have heterogenous
    types, the `primary_mrna` function will only discard mRNA features. This
    function, however, will discard all direct children of the gene that are
    not the primary transcript, including non-transcript children. This is a
    retty subtle distinction, and anecdotal experience suggests that cases in
    which the distinction actually matters are extremely rare.
    """
    for entry in entrystream:
        if not isinstance(entry, tag.Feature):
            yield entry
            continue

        for parent in tag.select.features(entry, parenttype, traverse=True):
            if parent.num_children == 0:
                continue

            transcripts = defaultdict(list)
            for child in parent.children:
                if child.type in type_terms:
                    transcripts[child.type].append(child)

            if len(transcripts) == 0:
                continue

            ttypes = list(transcripts.keys())
            ttype = _get_primary_type(ttypes, parent)
            transcript_list = transcripts[ttype]

            if ttype == 'mRNA':
                _emplace_pmrna(transcript_list, parent, strict=True)
            else:
                _emplace_transcript(transcript_list, parent)

        yield entry