def load(args):
    '''
    %prog load gff_file fasta_file [--options]

    Parses the selected features out of GFF, with subfeatures concatenated.
    For example, to get the CDS sequences, do this:
    $ %prog load athaliana.gff athaliana.fa --parents mRNA --children CDS

    To get 500bp upstream of a genes Transcription Start Site (TSS), do this:
    $ %prog load athaliana.gff athaliana.fa --feature=upstream:TSS:500

    Switch TSS with TrSS for Translation Start Site.
    '''
    from datetime import datetime as dt
    from jcvi.formats.fasta import Seq, SeqRecord

    # can request output fasta sequence id to be picked from following attributes
    valid_id_attributes = ["ID", "Name", "Parent", "Alias", "Target"]

    p = OptionParser(load.__doc__)
    p.add_option("--parents", dest="parents", default="mRNA",
            help="list of features to extract, use comma to separate (e.g." + \
            "'gene,mRNA') [default: %default]")
    p.add_option("--children", dest="children", default="CDS",
            help="list of features to extract, use comma to separate (e.g." + \
            "'five_prime_UTR,CDS,three_prime_UTR') [default: %default]")
    p.add_option("--feature", dest="feature",
            help="feature type to extract. e.g. `--feature=CDS` or " + \
            "`--feature=upstream:TSS:500` [default: %default]")
    p.add_option("--id_attribute", choices=valid_id_attributes,
            help="The attribute field to extract and use as FASTA sequence ID " + \
            "[default: %default]")
    p.add_option("--desc_attribute", default="Note",
            help="The attribute field to extract and use as FASTA sequence " + \
            "description [default: %default]")
    p.add_option("--full_header", default=None, choices=["default", "tair"],
            help="Specify if full FASTA header (with seqid, coordinates and datestamp)" + \
            " should be generated [default: %default]")

    g1 = OptionGroup(p, "Optional parameters (if generating full header)")
    g1.add_option("--sep", dest="sep", default=" ", \
            help="Specify separator used to delimiter header elements [default: \"%default\"]")
    g1.add_option("--datestamp", dest="datestamp", \
            help="Specify a datestamp in the format YYYYMMDD or automatically pick `today`" + \
            " [default: %default]")
    g1.add_option("--conf_class", dest="conf_class", default=False, action="store_true",
            help="Specify if `conf_class` attribute should be parsed and placed in the header" + \
            " [default: %default]")
    p.add_option_group(g1)

    p.set_outfile()

    opts, args = p.parse_args(args)

    if len(args) != 2:
        sys.exit(p.print_help())

    gff_file, fasta_file = args

    if opts.feature:
        opts.feature, opts.parent, opts.children, upstream_site, upstream_len, \
                flag, error_msg = parse_feature_param(opts.feature)
        if flag:
            sys.exit(error_msg)

    parents = set(opts.parents.split(','))
    children_list = set(opts.children.split(','))

    """
    In a situation where we want to extract sequence for only the top-level
    parent feature, specify feature type of parent == child
    """
    skipChildren = True if len(parents.symmetric_difference(children_list)) == 0 \
            else False

    id_attr = opts.id_attribute
    desc_attr = opts.desc_attribute
    sep = opts.sep

    import gffutils
    g = make_index(gff_file)
    f = Fasta(fasta_file, index=False)
    seqlen = {}
    for seqid, size in f.itersizes():
        seqlen[seqid] = size

    fw = must_open(opts.outfile, "w")

    for feat in get_parents(gff_file, parents):
        desc = ""
        if desc_attr:
            fparent = feat.attributes['Parent'][0] \
                if 'Parent' in feat.attributes else None
            if fparent:
                try:
                    g_fparent = g[fparent]
                except gffutils.exceptions.FeatureNotFoundError:
                    logging.error("{} not found in index .. skipped".format(fparent))
                    continue
                if desc_attr in g_fparent.attributes:
                    desc = ",".join(g_fparent.attributes[desc_attr])
            elif desc_attr in feat.attributes:
                desc = ",".join(feat.attributes[desc_attr])

        if opts.full_header:
            desc_parts = []
            desc_parts.append(desc)

            if opts.conf_class and 'conf_class' in feat.attributes:
                desc_parts.append(feat.attributes['conf_class'][0])

            if opts.full_header == "tair":
                orient = "REVERSE" if feat.strand == "-" else "FORWARD"
                feat_coords = "{0}:{1}-{2} {3} LENGTH=[LEN]".format(feat.seqid, \
                    feat.start, feat.end, orient)
            else:
                (s, e) = (feat.start, feat.end) if (feat.strand == "+") \
                        else (feat.end, feat.start)
                feat_coords = "{0}:{1}-{2}".format(feat.seqid, s, e)
            desc_parts.append(feat_coords)

            datestamp = opts.datestamp if opts.datestamp else \
                    "{0}{1}{2}".format(dt.now().year, dt.now().month, dt.now().day)
            desc_parts.append(datestamp)

            desc = sep.join(str(x) for x in desc_parts)
            desc = "".join(str(x) for x in (sep, desc)).strip()

        if opts.feature == "upstream":
            upstream_start, upstream_stop = get_upstream_coords(upstream_site, upstream_len, \
                     seqlen[feat.seqid], feat, children_list, g)

            if not upstream_start or not upstream_stop:
                continue

            feat_seq = f.sequence(dict(chr=feat.seqid, start=upstream_start,
                stop=upstream_stop, strand=feat.strand))

            (s, e) = (upstream_start, upstream_stop) \
                    if feat.strand == "+" else \
                     (upstream_stop, upstream_start)
            upstream_seq_loc = str(feat.seqid) + ":" + str(s) + "-" + str(e)
            desc = sep.join(str(x) for x in (desc, upstream_seq_loc, \
                    "FLANKLEN=" + str(upstream_len)))
        else:
            children = []
            if not skipChildren:
                for c in g.children(feat.id, 1):
                    if c.featuretype not in children_list:
                        continue
                    child = f.sequence(dict(chr=c.chrom, start=c.start, stop=c.stop,
                        strand=c.strand))
                    children.append((child, c))

                if not children:
                    print("[warning] %s has no children with type %s" \
                                            % (feat.id, ','.join(children_list)), file=sys.stderr)
                    continue
            else:
                child = f.sequence(dict(chr=feat.seqid, start=feat.start, stop=feat.end,
                    strand=feat.strand))
                children.append((child, feat))

            # sort children in incremental position
            children.sort(key=lambda x: x[1].start)
            # reverse children if negative strand
            if feat.strand == '-':
                children.reverse()
            feat_seq = ''.join(x[0] for x in children)

        desc = desc.replace("\"", "")

        id = ",".join(feat.attributes[id_attr]) if id_attr \
                and feat.attributes[id_attr] else \
                feat.id

        if opts.full_header == "tair":
            desc = desc.replace("[LEN]", str(len(feat_seq)))

        rec = SeqRecord(Seq(feat_seq), id=id, description=desc)
        SeqIO.write([rec], fw, "fasta")
        fw.flush()
def load ( args ) : from datetime import datetime as dt from jcvi . formats . fasta import Seq , SeqRecord valid_id_attributes = [ "ID" , "Name" , "Parent" , "Alias" , "Target" ] p = OptionParser ( load . __doc__ ) p . add_option ( "--parents" , dest = "parents" , default = "mRNA" , help = "list of features to extract, use comma to separate (e.g." + "'gene,mRNA') [default: %default]" ) p . add_option ( "--children" , dest = "children" , default = "CDS" , help = "list of features to extract, use comma to separate (e.g." + "'five_prime_UTR,CDS,three_prime_UTR') [default: %default]" ) p . add_option ( "--feature" , dest = "feature" , help = "feature type to extract. e.g. `--feature=CDS` or " + "`--feature=upstream:TSS:500` [default: %default]" ) p . add_option ( "--id_attribute" , choices = valid_id_attributes , help = "The attribute field to extract and use as FASTA sequence ID " + "[default: %default]" ) p . add_option ( "--desc_attribute" , default = "Note" , help = "The attribute field to extract and use as FASTA sequence " + "description [default: %default]" ) p . add_option ( "--full_header" , default = None , choices = [ "default" , "tair" ] , help = "Specify if full FASTA header (with seqid, coordinates and datestamp)" + " should be generated [default: %default]" ) g1 = OptionGroup ( p , "Optional parameters (if generating full header)" ) g1 . add_option ( "--sep" , dest = "sep" , default = " " , help = "Specify separator used to delimiter header elements [default: \"%default\"]" ) g1 . add_option ( "--datestamp" , dest = "datestamp" , help = "Specify a datestamp in the format YYYYMMDD or automatically pick `today`" + " [default: %default]" ) g1 . add_option ( "--conf_class" , dest = "conf_class" , default = False , action = "store_true" , help = "Specify if `conf_class` attribute should be parsed and placed in the header" + " [default: %default]" ) p . add_option_group ( g1 ) p . set_outfile ( ) opts , args = p . parse_args ( args ) if len ( args ) != 2 : sys . exit ( p . print_help ( ) ) gff_file , fasta_file = args if opts . feature : opts . feature , opts . parent , opts . children , upstream_site , upstream_len , flag , error_msg = parse_feature_param ( opts . feature ) if flag : sys . exit ( error_msg ) parents = set ( opts . parents . split ( ',' ) ) children_list = set ( opts . children . split ( ',' ) ) """
    In a situation where we want to extract sequence for only the top-level
    parent feature, specify feature type of parent == child
    """ skipChildren = True if len ( parents . symmetric_difference ( children_list ) ) == 0 else False id_attr = opts . id_attribute desc_attr = opts . desc_attribute sep = opts . sep import gffutils g = make_index ( gff_file ) f = Fasta ( fasta_file , index = False ) seqlen = { } for seqid , size in f . itersizes ( ) : seqlen [ seqid ] = size fw = must_open ( opts . outfile , "w" ) for feat in get_parents ( gff_file , parents ) : desc = "" if desc_attr : fparent = feat . attributes [ 'Parent' ] [ 0 ] if 'Parent' in feat . attributes else None if fparent : try : g_fparent = g [ fparent ] except gffutils . exceptions . FeatureNotFoundError : logging . error ( "{} not found in index .. skipped" . format ( fparent ) ) continue if desc_attr in g_fparent . attributes : desc = "," . join ( g_fparent . attributes [ desc_attr ] ) elif desc_attr in feat . attributes : desc = "," . join ( feat . attributes [ desc_attr ] ) if opts . full_header : desc_parts = [ ] desc_parts . append ( desc ) if opts . conf_class and 'conf_class' in feat . attributes : desc_parts . append ( feat . attributes [ 'conf_class' ] [ 0 ] ) if opts . full_header == "tair" : orient = "REVERSE" if feat . strand == "-" else "FORWARD" feat_coords = "{0}:{1}-{2} {3} LENGTH=[LEN]" . format ( feat . seqid , feat . start , feat . end , orient ) else : ( s , e ) = ( feat . start , feat . end ) if ( feat . strand == "+" ) else ( feat . end , feat . start ) feat_coords = "{0}:{1}-{2}" . format ( feat . seqid , s , e ) desc_parts . append ( feat_coords ) datestamp = opts . datestamp if opts . datestamp else "{0}{1}{2}" . format ( dt . now ( ) . year , dt . now ( ) . month , dt . now ( ) . day ) desc_parts . append ( datestamp ) desc = sep . join ( str ( x ) for x in desc_parts ) desc = "" . join ( str ( x ) for x in ( sep , desc ) ) . strip ( ) if opts . feature == "upstream" : upstream_start , upstream_stop = get_upstream_coords ( upstream_site , upstream_len , seqlen [ feat . seqid ] , feat , children_list , g ) if not upstream_start or not upstream_stop : continue feat_seq = f . sequence ( dict ( chr = feat . seqid , start = upstream_start , stop = upstream_stop , strand = feat . strand ) ) ( s , e ) = ( upstream_start , upstream_stop ) if feat . strand == "+" else ( upstream_stop , upstream_start ) upstream_seq_loc = str ( feat . seqid ) + ":" + str ( s ) + "-" + str ( e ) desc = sep . join ( str ( x ) for x in ( desc , upstream_seq_loc , "FLANKLEN=" + str ( upstream_len ) ) ) else : children = [ ] if not skipChildren : for c in g . children ( feat . id , 1 ) : if c . featuretype not in children_list : continue child = f . sequence ( dict ( chr = c . chrom , start = c . start , stop = c . stop , strand = c . strand ) ) children . append ( ( child , c ) ) if not children : print ( "[warning] %s has no children with type %s" % ( feat . id , ',' . join ( children_list ) ) , file = sys . stderr ) continue else : child = f . sequence ( dict ( chr = feat . seqid , start = feat . start , stop = feat . end , strand = feat . strand ) ) children . append ( ( child , feat ) ) children . sort ( key = lambda x : x [ 1 ] . start ) if feat . strand == '-' : children . reverse ( ) feat_seq = '' . join ( x [ 0 ] for x in children ) desc = desc . replace ( "\"" , "" ) id = "," . join ( feat . attributes [ id_attr ] ) if id_attr and feat . attributes [ id_attr ] else feat . id if opts . full_header == "tair" : desc = desc . replace ( "[LEN]" , str ( len ( feat_seq ) ) ) rec = SeqRecord ( Seq ( feat_seq ) , id = id , description = desc ) SeqIO . write ( [ rec ] , fw , "fasta" ) fw . flush ( )
%prog load gff_file fasta_file [--options]

    Parses the selected features out of GFF, with subfeatures concatenated.
    For example, to get the CDS sequences, do this:
    $ %prog load athaliana.gff athaliana.fa --parents mRNA --children CDS

    To get 500bp upstream of a genes Transcription Start Site (TSS), do this:
    $ %prog load athaliana.gff athaliana.fa --feature=upstream:TSS:500

    Switch TSS with TrSS for Translation Start Site.