{"action":"created","comment":{"url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/comments/52105893","id":52105893,"diff_hunk":"@@ -0,0 +1,826 @@\n+export StrucElement,\n+    AbstractAtom,\n+    Atom,\n+    DisorderedAtom,\n+    AbstractResidue,\n+    Residue,\n+    DisorderedResidue,\n+    Chain,\n+    Model,\n+    Structure,\n+    AtomList,\n+    ResidueList,\n+    ChainList,\n+    ModelList,\n+    StrucElementOrList,\n+    ishetatom,\n+    getserial,\n+    getatomname,\n+    getaltlocid,\n+    getresname,\n+    getchainid,\n+    getresnumber,\n+    getinscode,\n+    getx,\n+    gety,\n+    getz,\n+    getcoords,\n+    getoccupancy,\n+    gettempfac,\n+    getelement,\n+    getcharge,\n+    setx!,\n+    sety!,\n+    setz!,\n+    setcoords!,\n+    getdefaultaltlocid,\n+    getdefaultatom,\n+    getaltlocids,\n+    ishetero,\n+    setdefaultaltlocid!,\n+    getresid,\n+    ishetres,\n+    getatomnames,\n+    getdisorderedres,\n+    getdefaultresname,\n+    getdefaultresidue,\n+    getresnames,\n+    setdefaultresname!,\n+    getresids,\n+    getmodelnumber,\n+    getchainids,\n+    getstrucname,\n+    getmodelnumbers,\n+    setstrucname!,\n+    applyselectors,\n+    applyselectors!,\n+    collectresidues,\n+    collectatoms,\n+    countmodels,\n+    countchains,\n+    countresidues,\n+    countatoms,\n+    isdisordered,\n+    organise,\n+    formatomlist,\n+    choosedefaultaltlocid,\n+    organisemodel,\n+    organisestruc,\n+    stdatomselector,\n+    hetatomselector,\n+    atomnameselector,\n+    calpha_atom_names,\n+    calphaselector,\n+    backbone_atom_names,\n+    backboneselector,\n+    heavy_atom_names,\n+    heavyatomselector,\n+    resnameselector,\n+    water_res_names,\n+    waterselector,\n+    stdresselector,\n+    hetresselector,\n+    disorderselector,\n+    hydrogenselector\n+\n+\n+import Base: getindex,\n+    setindex!,\n+    length,\n+    start,\n+    next,\n+    done,\n+    eltype,\n+    show\n+\n+\n+\"\"\"A protein structural element.\"\"\"\n+abstract StrucElement\n+\n+\n+\"\"\"An atom represented in a PDB file - either an `Atom` or a\n+`DisorderedAtom`.\"\"\"\n+abstract AbstractAtom <: StrucElement\n+\n+\"\"\"An atom record from a PDB file.\"\"\"\n+immutable Atom <: AbstractAtom\n+    het_atom::Bool\n+    serial::Int\n+    name::AbstractString\n+    alt_loc_id::Char\n+    res_name::AbstractString\n+    chain_id::Char\n+    res_no::Int\n+    ins_code::Char\n+    coords::Array{Float64,1}\n+    occupancy::Float64\n+    temp_fac::Float64\n+    element::AbstractString\n+    charge::AbstractString\n+end\n+\n+\"\"\"A container to hold different versions of the same atom.\"\"\"\n+immutable DisorderedAtom <: AbstractAtom\n+    alt_loc_ids::Dict{Char, Atom}\n+    default::Char\n+end\n+\n+\n+\"\"\"A residue (amino acid) or other molecule from a PDB file.\"\"\"\n+abstract AbstractResidue <: StrucElement\n+\n+\"\"\"A residue (amino acid) or other molecule from a PDB file.\"\"\"\n+immutable Residue <: AbstractResidue\n+    name::AbstractString\n+    chain_id::Char\n+    number::Int\n+    ins_code::Char\n+    het_res::Bool # Does the residue consist of hetatoms?\n+    atom_list::Array{AbstractString,1}\n+    atoms::Dict{AbstractString, AbstractAtom}\n+end\n+\n+Residue(name::AbstractString, chain_id::Char, number::Int, ins_code::Char, het_res::Bool) = Residue(name, chain_id, number, ins_code, het_res, AbstractString[], Dict())\n+Residue(atom::AbstractAtom) = Residue(getresname(atom), getchainid(atom), getresnumber(atom), getinscode(atom), ishetatom(atom))\n+\n+\"\"\"A container to hold different versions of the same residue\n+(point mutations).\"\"\"\n+immutable DisorderedResidue <: AbstractResidue\n+    names::Dict{AbstractString, Residue}\n+    default::AbstractString\n+end\n+\n+\n+\"\"\"A chain from a PDB file.\"\"\"\n+immutable Chain <: StrucElement\n+    id::Char\n+    res_list::Array{AbstractString,1}\n+    residues::Dict{AbstractString, AbstractResidue}\n+end\n+\n+Chain(id::Char) = Chain(id, Dict())\n+\n+\n+\"\"\"A model from a PDB file.\"\"\"\n+immutable Model <: StrucElement\n+    number::Int\n+    chains::Dict{Char, Chain}\n+end\n+\n+Model(number::Int) = Model(number, Dict())\n+\n+\n+\"\"\"A container for multiple models from a PDB file.\"\"\"\n+immutable Structure <: StrucElement\n+    name::AbstractString\n+    models::Dict{Int, Model}\n+end\n+\n+Structure(name::AbstractString) = Structure(name, Dict())\n+\n+\n+\"\"\"An `Array{AbstractAtom,1}`.\"\"\"\n+typealias AtomList Array{AbstractAtom,1}\n+\n+\"\"\"An `Array{AbstractResidue,1}`.\"\"\"\n+typealias ResidueList Array{AbstractResidue,1}\n+\n+\"\"\"An `Array{Chain,1}`.\"\"\"\n+typealias ChainList Array{Chain,1}\n+\n+\"\"\"An `Array{Model,1}`.\"\"\"\n+typealias ModelList Array{Model,1}\n+\n+\"\"\"A protein structural element or list of structural elements.\"\"\"\n+typealias StrucElementOrList Union{StrucElement, AtomList, ResidueList, ChainList, ModelList}\n+\n+\n+# Allow accessing sub elements contained in an element like a dictionary\n+# e.g. allows you to do res[atom_name] rather than res.atoms[atom_name]\n+\n+# Accessing a DisorderedAtom with a character returns the Atom with that alt loc ID\n+getindex(disordered_atom::DisorderedAtom, alt_loc_id::Char) = disordered_atom.alt_loc_ids[alt_loc_id]\n+function setindex!(disordered_atom::DisorderedAtom, atom::Atom, alt_loc_id::Char)\n+    disordered_atom.alt_loc_ids[alt_loc_id] = atom\n+end\n+\n+# Accessing a Residue with an AbstractString returns the AbstractAtom with that atom name\n+getindex(res::Residue, atom_name::AbstractString) = res.atoms[atom_name]\n+function setindex!(res::Residue, atom::AbstractAtom, atom_name::AbstractString)\n+    res.atoms[atom_name] = atom\n+end\n+\n+# Accessing a DisorderedResidue with an AbstractString returns the AbstractAtom in the default Residue with that atom name\n+# This is not necessarily intuitive, it may be expected to return the Residue with that residue name\n+# However this way accessing an AbstractResidue always returns an AbstractAtom\n+getindex(disordered_res::DisorderedResidue, atom_name::AbstractString) = disordered_res.names[disordered_res.default][atom_name]\n+function setindex!(disordered_res::DisorderedResidue, atom::AbstractAtom, atom_name::AbstractString)\n+    disordered_res.names[disordered_res.default][atom_name] = atom\n+end\n+\n+# Accessing a Chain with an AbstractString returns the AbstractResidue with that residue ID\n+getindex(chain::Chain, res_id::AbstractString) = chain.residues[res_id]\n+function setindex!(chain::Chain, res::AbstractResidue, res_id::AbstractString)\n+    chain.residues[res_id] = res\n+end\n+\n+# Accessing a Chain with an Int returns the AbstractResidue with that residue ID converted to a string\n+getindex(chain::Chain, res_no::Int) = chain.residues[string(res_no)]\n+function setindex!(chain::Chain, res::AbstractResidue, res_no::Int)\n+    chain.residues[string(res_no)] = res\n+end\n+\n+# Accessing a Model with a Char returns the Chain with that chain ID\n+getindex(model::Model, chain_id::Char) = model.chains[chain_id]\n+function setindex!(model::Model, chain::Chain, chain_id::Char)\n+    model.chains[chain_id] = chain\n+end\n+\n+# Accessing a Structure with an Int returns the Model with that model number\n+getindex(struc::Structure, model_no::Int) = struc.models[model_no]\n+function setindex!(struc::Structure, model::Model, model_no::Int)\n+    struc.models[model_no] = model\n+end\n+\n+# Accessing a Structure with a Char returns the Chain with that chain ID on model number 1\n+getindex(struc::Structure, chain_id::Char) = struc.models[1][chain_id]\n+function setindex!(struc::Structure, chain::Chain, chain_id::Char)\n+    struc.models[1][chain_id] = chain\n+end\n+\n+\n+# Getters and setters for structural elements\n+\n+ishetatom(atom::Atom) = atom.het_atom\n+getserial(atom::Atom) = atom.serial\n+getatomname(atom::Atom) = atom.name\n+getaltlocid(atom::Atom) = atom.alt_loc_id\n+getresname(atom::Atom) = atom.res_name\n+getchainid(atom::Atom) = atom.chain_id\n+getresnumber(atom::Atom) = atom.res_no\n+getinscode(atom::Atom) = atom.ins_code\n+getx(atom::Atom) = atom.coords[1]\n+gety(atom::Atom) = atom.coords[2]\n+getz(atom::Atom) = atom.coords[3]\n+getcoords(atom::Atom) = atom.coords\n+getoccupancy(atom::Atom) = atom.occupancy\n+gettempfac(atom::Atom) = atom.temp_fac\n+getelement(atom::Atom) = atom.element\n+getcharge(atom::Atom) = atom.charge\n+\n+setx!(atom::Atom, x::Real) = (atom.coords[1] = x; nothing)\n+sety!(atom::Atom, y::Real) = (atom.coords[2] = y; nothing)\n+setz!(atom::Atom, z::Real) = (atom.coords[3] = z; nothing)\n+\n+function setcoords!(atom::Atom, coords::Array{Float64,1})\n+    @assert length(coords) == 3 \"3 coordinates must be given\"\n+    setx!(atom, coords[1])\n+    sety!(atom, coords[2])\n+    setz!(atom, coords[3])\n+    return nothing\n+end\n+\n+getdefaultaltlocid(disordered_atom::DisorderedAtom) = disordered_atom.default\n+getdefaultatom(disordered_atom::DisorderedAtom) = disordered_atom[getdefaultaltlocid(disordered_atom)]\n+getaltlocids(disordered_atom::DisorderedAtom) = sort(collect(keys(disordered_atom.alt_loc_ids)), by= alt_loc_id -> getserial(disordered_atom[alt_loc_id]))\n+\n+ishetatom(disordered_atom::DisorderedAtom) = ishetatom(getdefaultatom(disordered_atom))\n+getserial(disordered_atom::DisorderedAtom) = getserial(getdefaultatom(disordered_atom))\n+getatomname(disordered_atom::DisorderedAtom) = getatomname(getdefaultatom(disordered_atom))\n+getaltlocid(disordered_atom::DisorderedAtom) = getdefaultaltlocid(disordered_atom)\n+getresname(disordered_atom::DisorderedAtom) = getresname(getdefaultatom(disordered_atom))\n+getchainid(disordered_atom::DisorderedAtom) = getchainid(getdefaultatom(disordered_atom))\n+getresnumber(disordered_atom::DisorderedAtom) = getresnumber(getdefaultatom(disordered_atom))\n+getinscode(disordered_atom::DisorderedAtom) = getinscode(getdefaultatom(disordered_atom))\n+getx(disordered_atom::DisorderedAtom) = getx(getdefaultatom(disordered_atom))\n+gety(disordered_atom::DisorderedAtom) = gety(getdefaultatom(disordered_atom))\n+getz(disordered_atom::DisorderedAtom) = getz(getdefaultatom(disordered_atom))\n+getcoords(disordered_atom::DisorderedAtom) = getcoords(getdefaultatom(disordered_atom))\n+getoccupancy(disordered_atom::DisorderedAtom) = getoccupancy(getdefaultatom(disordered_atom))\n+gettempfac(disordered_atom::DisorderedAtom) = gettempfac(getdefaultatom(disordered_atom))\n+getelement(disordered_atom::DisorderedAtom) = getelement(getdefaultatom(disordered_atom))\n+getcharge(disordered_atom::DisorderedAtom) = getcharge(getdefaultatom(disordered_atom))\n+\n+ishetero(atom::AbstractAtom) = ishetatom(atom)\n+\n+function setdefaultaltlocid!(disordered_atom::DisorderedAtom, alt_loc_id::Char)\n+    @assert alt_loc_id in getaltlocids(disordered_atom) \"The new default alternative location ID must be present in the atom\"\n+    disordered_atom = DisorderedAtom(disordered_atom.alt_loc_ids, alt_loc_id)\n+end\n+\n+# These coordinate setters only set the default atom coordinates\n+setx!(disordered_atom::DisorderedAtom, x::Real) = setx!(getdefaultatom(disordered_atom), x)\n+sety!(disordered_atom::DisorderedAtom, y::Real) = sety!(getdefaultatom(disordered_atom), y)\n+setz!(disordered_atom::DisorderedAtom, z::Real) = setz!(getdefaultatom(disordered_atom), z)\n+setcoords!(disordered_atom::DisorderedAtom, coords::Array{Float64,1}) = setcoords!(getdefaultatom(disordered_atom), coords)\n+\n+\n+function getresid(element::Union{AbstractResidue, AbstractAtom}; full::Bool=false)\n+    res_id = strip(\"$(getresnumber(element))$(getinscode(element))\")\n+    ishetero(element) ? res_id = \"H_$res_id\" : nothing\n+    full ? res_id = \"$(res_id):$(getchainid(element))\" : nothing\n+    return res_id\n+end\n+\n+\n+getresname(res::Residue) = res.name\n+getchainid(res::Residue) = res.chain_id\n+getresnumber(res::Residue) = res.number\n+getinscode(res::Residue) = res.ins_code\n+ishetres(res::Residue) = res.het_res\n+getatomnames(res::Residue) = res.atom_list\n+\n+getdisorderedres(disordered_res::DisorderedResidue, res_name::AbstractString) = disordered_res.names[res_name]\n+getdefaultresname(disordered_res::DisorderedResidue) = disordered_res.default\n+getdefaultresidue(disordered_res::DisorderedResidue) = disordered_res.names[getdefaultresname(disordered_res)]\n+getresnames(disordered_res::DisorderedResidue) = sort(collect(keys(disordered_res.names)))\n+\n+getresname(disordered_res::DisorderedResidue) = getdefaultresname(disordered_res)\n+getchainid(disordered_res::DisorderedResidue) = getchainid(getdefaultresidue(disordered_res))\n+getresnumber(disordered_res::DisorderedResidue) = getresnumber(getdefaultresidue(disordered_res))\n+getinscode(disordered_res::DisorderedResidue) = getinscode(getdefaultresidue(disordered_res))\n+ishetres(disordered_res::DisorderedResidue) = ishetres(getdefaultresidue(disordered_res))\n+getatomnames(disordered_res::DisorderedResidue) = getatomnames(getdefaultresidue(disordered_res))\n+\n+ishetero(res::AbstractResidue) = ishetres(res)\n+\n+function setdefaultresname!(disordered_res::DisorderedResidue, res_name::AbstractString)\n+    @assert res_name in getresnames(disordered_res) \"The new default residue name must be present in the residue\"\n+    disordered_res = DisorderedResidue(disordered_res.names, res_name)\n+end\n+\n+\n+getchainid(chain::Chain) = chain.id\n+\n+getresids(chain::Chain) = chain.res_list\n+\n+function sortresids(residues::Dict{AbstractString, AbstractResidue})\n+    sorted_res_ids = sort(collect(keys(residues)), by= res_id -> getinscode(residues[res_id]))\n+    sort!(sorted_res_ids, by= res_id -> getresnumber(residues[res_id]))\n+    sort!(sorted_res_ids, by= res_id -> ishetres(residues[res_id]))\n+    return sorted_res_ids\n+end\n+\n+\n+getmodelnumber(model::Model) = model.number\n+getchainids(model::Model) = sort(collect(keys(model.chains)))\n+\n+\n+getstrucname(struc::Structure) = struc.name\n+getmodelnumbers(struc::Structure) = sort(collect(keys(struc.models)))\n+\n+setstrucname!(struc::Structure, name::AbstractString) = struc.name = name\n+\n+\n+# Iterators to yield sub elements when looping over an element\n+\n+# Iterating over a Structure yields Models\n+length(struc::Structure) = length(getmodelnumbers(struc))\n+start(::Structure) = 1\n+next(struc::Structure, state) = (struc[getmodelnumbers(struc)[state]], state + 1)\n+done(struc::Structure, state) = state > length(struc)\n+eltype(::Type{Structure}) = Model\n+\n+# Iterating over a Model yields Chains\n+length(model::Model) = length(getchainids(model))\n+start(::Model) = 1\n+next(model::Model, state) = (model[getchainids(model)[state]], state + 1)\n+done(model::Model, state) = state > length(model)\n+eltype(::Type{Model}) = Chain\n+\n+# Iterating over a Chain yields AbstractResidues\n+length(chain::Chain) = length(getresids(chain))\n+start(::Chain) = 1\n+next(chain::Chain, state) = (chain[getresids(chain)[state]], state + 1)\n+done(chain::Chain, state) = state > length(chain)\n+eltype(::Type{Chain}) = AbstractResidue\n+\n+# Iterating over a Residue yields AbstractAtoms\n+length(res::Residue) = length(getatomnames(res))\n+start(::Residue) = 1\n+next(res::Residue, state) = (res[getatomnames(res)[state]], state + 1)\n+done(res::Residue, state) = state > length(res)\n+eltype(::Type{Residue}) = AbstractAtom\n+\n+# Iterating over a DisorderedResidue yields AbstractAtoms\n+# This is not necessarily intuitive, it may be expected to yield Residues\n+# However this way iterating over an AbstractResidue always yields AbstractAtoms\n+length(disordered_res::DisorderedResidue) = length(getatomnames(disordered_res))\n+start(::DisorderedResidue) = 1\n+next(disordered_res::DisorderedResidue, state) = (getdefaultresidue(disordered_res)[getatomnames(disordered_res)[state]], state + 1)\n+done(disordered_res::DisorderedResidue, state) = state > length(disordered_res)\n+eltype(::Type{DisorderedResidue}) = AbstractAtom\n+\n+# Iterating over an Atom returns itself\n+# This is not necessarily intuitive, it may be expected to not be an iterator\n+# However this way iterating over an AbstractAtom always yields Atoms\n+length(atom::Atom) = 1\n+start(::Atom) = 1\n+next(atom::Atom, state) = (atom, state + 1)\n+done(atom::Atom, state) = state > 1\n+eltype(::Type{Atom}) = Atom\n+\n+# Iterating over a DisorderedAtom yields Atoms\n+length(disordered_atom::DisorderedAtom) = length(getaltlocids(disordered_atom))\n+start(::DisorderedAtom) = 1\n+next(disordered_atom::DisorderedAtom, state) = (disordered_atom[getaltlocids(disordered_atom)[state]], state + 1)\n+done(disordered_atom::DisorderedAtom, state) = state > length(disordered_atom)\n+eltype(::Type{DisorderedAtom}) = Atom\n+\n+\n+\"\"\"Returns a copy of a `ResidueList` or `AtomList` with all elements that do not\n+satisfy `args...` removed.\"\"\"\n+function applyselectors(element_list::Union{ResidueList, AtomList}, args...)\n+    new_list = copy(element_list)\n+    applyselectors!(new_list, args...)\n+    return new_list\n+end\n+\n+\"\"\"Runs `applyselectors` in place.\"\"\"\n+function applyselectors!(element_list::Union{ResidueList, AtomList}, args...)\n+    for selector_function in args\n+        filter!(selector_function, element_list)\n+    end\n+end\n+\n+applyselectors(element_list::Union{ResidueList, AtomList}) = element_list\n+applyselectors!(element_list::Union{ResidueList, AtomList}) = element_list\n+\n+\n+\"\"\"Returns a `ResidueList` of the residues in an element.\n+Only elements that satisfy `args...` are returned.\"\"\"\n+collectresidues(struc::Structure, args...) = collectresidues(struc[1], args...)\n+collectresidues(chain::Chain, args...) = applyselectors(collect(chain), args...)\n+collectresidues(res::AbstractResidue, args...) = applyselectors(AbstractResidue[res], args...)\n+collectresidues(atom::AbstractAtom, args...) = applyselectors(organise(atom), args...)\n+collectresidues(residues::ResidueList, args...) = applyselectors(residues, args...)\n+collectresidues(atoms::AtomList, args...) = applyselectors(organise(atoms), args...)\n+\n+function collectresidues(element::Union{Model, ModelList, ChainList}, args...)\n+    residues = AbstractResidue[]\n+    for sub_element in element\n+        append!(residues, collectresidues(sub_element, args...))\n+    end\n+    return residues\n+end\n+\n+\n+\"\"\"Return an `AtomList` of the atoms in an element.\n+Only elements that satisfy `args...` are returned.\"\"\"\n+collectatoms(struc::Structure, args...) = collectatoms(struc[1], args...)\n+collectatoms(res::AbstractResidue, args...) = applyselectors(collect(res), args...)\n+collectatoms(atom::AbstractAtom, args...) = applyselectors(AbstractAtom[atom], args...)\n+collectatoms(atoms::AtomList, args...) = applyselectors(atoms, args...)\n+\n+function collectatoms(element::Union{Model, Chain, ModelList, ChainList, ResidueList}, args...)\n+    atoms = AbstractAtom[]\n+    for sub_element in element\n+        append!(atoms, collectatoms(sub_element, args...))\n+    end\n+    return atoms\n+end\n+\n+\n+countmodels(struc::Structure) = length(struc)\n+countchains(struc::Structure) = length(struc[1])\n+countchains(chain::Chain) = length(chain)\n+countresidues(element::StrucElementOrList, args...) = length(collectresidues(element, args...))\n+countatoms(element::StrucElementOrList, args...) = length(collectatoms(element, args...))\n+\n+\n+# Returns true if the number of DisorderedAtoms or DisorderedResidues is greater than 0\n+# This needs some thinking about as does not necessarily correspond to a non-blank alt loc ID\n+isdisordered(element::StrucElementOrList) = countatoms(element, disorderselector) > 0 || countresidues(element, disorderselector) > 0\n+\n+\n+\"\"\"Organise a `StrucElementOrList` into the next level up the heirarchy. An\n+`AtomList` becomes a `ResidueList`, a `ResidueList` becomes a `ChainList`, a\n+`ChainList` becomes a `Model` and a `ModelList` becomes a `Structure`.\"\"\"\n+function organise(models::ModelList; struc_name::AbstractString=\"\")\n+    # Organise a ModelList into a Structure\n+    struc = Structure(struc_name)\n+    for model in models\n+        @assert !(getmodelnumber(model) in getmodelnumbers(struc)) \"Multiple models with the same model number found - cannot organise into a structure\"\n+        struc[getmodelnumber(model)] = model\n+    end\n+    return struc\n+end\n+\n+\n+# Organise a ChainList into a Model\n+function organise(chains::ChainList; model_number::Int=1)\n+    model = Model(model_number)\n+    for chain in chains\n+        @assert !(getchainid(chain) in getchainids(model)) \"Multiple chains with the same chain ID found - cannot organise into a model\"\n+        model[getchainid(chain)] = chain\n+    end\n+    return model\n+end\n+\n+\n+# Organise a ResidueList into a ChainList\n+function organise(residues::ResidueList)\n+    chains = Dict{Char, Dict{AbstractString, AbstractResidue}}()\n+    for res in residues\n+        chain_id = getchainid(res)\n+        !(chain_id in keys(chains)) ? chains[chain_id] = Dict{AbstractString, AbstractResidue}() : nothing\n+        res_id = getresid(res)\n+        @assert !(res_id in keys(chains[chain_id])) \"Multiple residues with the same residue ID found - cannot organise into chains\"\n+        chains[chain_id][res_id] = res\n+    end\n+    return [Chain(chain_id, sortresids(chains[chain_id]), chains[chain_id]) for chain_id in sort(collect(keys(chains)))]\n+end\n+\n+\n+# Organise an AtomList into a ResidueList\n+# Perhaps store chain as well so can be properly sorted\n+function organise(atoms::AtomList)\n+    # Key is residue ID, value is list of atoms\n+    residues = Dict{AbstractString, AtomList}()\n+    # Key is residue ID, value is Dict where key is residue name and value is list of atoms\n+    disordered_residues = Dict{AbstractString, Dict{AbstractString, AtomList}}()\n+    # Key is residue ID, value is default residue name\n+    defaults = Dict{AbstractString, AbstractString}()\n+    for atom in atoms\n+        res_id = getresid(atom; full=true)\n+        if !(res_id in keys(residues)) && !(res_id in keys(disordered_residues))\n+            residues[res_id] = AbstractAtom[]\n+            current_residue = residues[res_id]\n+        # The disordered residue container could already exist\n+        elseif res_id in keys(disordered_residues)\n+            # If the res name isn't present, need to add a new Residue\n+            !(getresname(atom) in keys(disordered_residues[res_id])) ? disordered_residues[res_id][getresname(atom)] = AbstractAtom[] : nothing\n+            current_residue = disordered_residues[res_id][getresname(atom)]\n+        # The disorered residue container doesn't exist and needs creating\n+        elseif getresname(atom) != getresname(residues[res_id][1])\n+            disordered_residues[res_id] = Dict(\n+                getresname(residues[res_id][1]) => residues[res_id],\n+                getresname(atom) => AbstractAtom[]\n+            )\n+            # The default res name is the first added\n+            defaults[res_id] = getresname(residues[res_id][1])\n+            # Remove Residue now we have created a DisorderedResidue\n+            delete!(residues, res_id)\n+            current_residue = disordered_residues[res_id][getresname(atom)]\n+        else\n+            current_residue = residues[res_id]\n+        end\n+        atom_name = getatomname(atom)\n+        # Fix quote and atom name check\n+        index_found = 0\n+        for i in eachindex(current_residue)\n+            atom_name == getatomname(current_residue[i]) ? (index_found = i; break) : nothing\n+        end\n+        @assert index_found == 0 \"Multiple atoms with the same atom name on the same residue - cannot organise into residues:\\n$(current_residue[index_found])\\n$(atom)\"\n+        push!(current_residue, atom)\n+    end\n+    residues_out = AbstractResidue[]\n+    for res_id in keys(residues)\n+        atoms = sort(residues[res_id], by= getserial)\n+        push!(residues_out, Residue(\n+            getresname(atoms[1]),\n+            getchainid(atoms[1]),\n+            getresnumber(atoms[1]),\n+            getinscode(atoms[1]),\n+            ishetero(atoms[1]),\n+            map(getatomname, atoms),\n+            [getatomname(atom) => atom for atom in atoms]\n+        ))\n+    end\n+    for res_id in keys(disordered_residues)\n+        new_disordered_res = DisorderedResidue(Dict(), defaults[res_id])\n+        for res_name in keys(disordered_residues[res_id])\n+            atoms = sort(disordered_residues[res_id][res_name], by= getserial)\n+            # Hacky setter\n+            new_disordered_res.names[res_name] = Residue(\n+                getresname(atoms[1]),\n+                getchainid(atoms[1]),\n+                getresnumber(atoms[1]),\n+                getinscode(atoms[1]),\n+                ishetero(atoms[1]),\n+                map(getatomname, atoms),\n+                [getatomname(atom) => atom for atom in atoms]\n+            )\n+        end\n+        push!(residues_out, new_disordered_res)\n+    end\n+    return residues_out\n+end\n+\n+\n+organise(model::Model; struc_name::AbstractString=\"\") = organise([model]; struc_name=struc_name)\n+organise(chain::Chain; model_number::Int=1) = organise([chain]; model_number=model_number)\n+organise(res::AbstractResidue) = organise(AbstractResidue[res])\n+organise(atom::AbstractAtom) = organise(AbstractAtom[atom])\n+\n+\n+# Should check none of the atoms are DisorderedAtoms already, or enforce Array{Atom,1}\n+\"\"\"Form a list of `AbstractAtom`s from a list of `Atom`s. Combines disordered\n+atoms into disordered atom containers unless `remove_disorder` is `true`, in\n+which case removes all but one location for disordered atoms.\"\"\"\n+function formatomlist(atoms::AtomList; remove_disorder::Bool=false)\n+    # Key is (residue ID, residue name, atom name)\n+    # Remove this to a separate function getatomid?\n+    atom_dic = Dict{Tuple{AbstractString, AbstractString, AbstractString}, AbstractAtom}()\n+    for atom in atoms\n+        #@assert typeof(atom) != DisorderedAtom \"There is already a DisorderedAtom container in the AtomList\"\n+        atom_id = (getresid(atom; full=true), getresname(atom), getatomname(atom))\n+        # The atom does not exist so we can create it\n+        if !(atom_id in keys(atom_dic))\n+            atom_dic[atom_id] = atom\n+        # A disordered atom container already exists and the alt loc ID is not taken\n+        elseif typeof(atom_dic[atom_id]) == DisorderedAtom && !(getaltlocid(atom) in getaltlocids(atom_dic[atom_id]))\n+            # Add the new atom to the disordered atom container\n+            atom_dic[atom_id][getaltlocid(atom)] = atom\n+            # If the default alt loc requires changing, change it\n+            choosedefaultaltlocid(getdefaultatom(atom_dic[atom_id]), atom) != getdefaultaltlocid(atom_dic[atom_id]) ? setdefaultaltlocid!(atom_dic[atom_id], getaltlocid(atom)) : nothing\n+        # The atom already exists and the alt loc IDs are different\n+        elseif typeof(atom_dic[atom_id]) == Atom && getaltlocid(atom) != getaltlocid(atom_dic[atom_id])\n+            # If we are removing disorder and the new atom is preferred to the old one, replace the old one\n+            if remove_disorder && choosedefaultaltlocid(atom, atom_dic[atom_id]) == getaltlocid(atom)\n+                atom_dic[atom_id] = atom\n+            # If we are not removing disorder, create a new disordered atom container and add both atoms\n+            else\n+                atom_dic[atom_id] = DisorderedAtom(Dict(\n+                    getaltlocid(atom) => atom,\n+                    getaltlocid(atom_dic[atom_id]) => atom_dic[atom_id]\n+                ), choosedefaultaltlocid(atom, atom_dic[atom_id]))\n+            end\n+        else\n+            error(\"Two copies of the same atom have the same alternative location ID (note names are stripped of whitespace so identical names with different spacing, e.g. ' CA ' and 'CA  ', are not currently supported):\\n$(atom_dic[atom_id])\\n$(atom)\")\n+        end\n+    end\n+    return sort(collect(values(atom_dic)), by=getserial)\n+end\n+\n+\n+\"\"\"Determine which of two `Atom`s representing a disorered atom better qualifies\n+as the default location.\n+The `Atom` with the highest occupancy is chosen; in the case of ties the `Atom`\n+with the lowest alternative location ID in alphabetical order is chosen.\"\"\"\n+function choosedefaultaltlocid(atom_one::Atom, atom_two::Atom)\n+    if getoccupancy(atom_one) > getoccupancy(atom_two) ||\n+            (getoccupancy(atom_one) == getoccupancy(atom_two) &&\n+            Int(getaltlocid(atom_one)) < Int(getaltlocid(atom_two)))\n+        return getaltlocid(atom_one)\n+    else\n+        return getaltlocid(atom_two)\n+    end\n+end\n+\n+\n+# These can possibly be put as a Union\n+\"\"\"Organise elements into a `Model`.\"\"\"\n+organisemodel(chains::ChainList; model_number::Int=1) = organise(chains; model_number=model_number)\n+organisemodel(residues::ResidueList; model_number::Int=1) = organise(organise(residues); model_number=model_number)\n+organisemodel(atoms::AtomList; model_number::Int=1) = organise(organise(organise(atoms)); model_number=model_number)\n+organisemodel(chain::Chain; model_number::Int=1) = organisemodel([chain]; model_number=model_number)\n+organisemodel(res::AbstractResidue; model_number::Int=1) = organisemodel(AbstractResidue[res]; model_number=model_number)\n+organisemodel(atom::AbstractAtom; model_number::Int=1) = organisemodel(AbstractAtom[atom]; model_number=model_number)\n+\n+\n+\"\"\"Organise elements into a `Structure`.\"\"\"\n+organisestruc(models::ModelList; struc_name::AbstractString=\"\") = organise(models; struc_name=struc_name)\n+organisestruc(chains::ChainList; struc_name::AbstractString=\"\", model_number::Int=1) = organise(organisemodel(chains; model_number=model_number); struc_name=struc_name)\n+organisestruc(residues::ResidueList; struc_name::AbstractString=\"\", model_number::Int=1) = organise(organisemodel(residues; model_number=model_number); struc_name=struc_name)\n+organisestruc(atoms::AtomList; struc_name::AbstractString=\"\", model_number::Int=1) = organise(organisemodel(atoms; model_number=model_number); struc_name=struc_name)\n+organisestruc(model::Model; struc_name::AbstractString=\"\") = organisestruc([model]; struc_name=struc_name)\n+organisestruc(chain::Chain; struc_name::AbstractString=\"\", model_number::Int=1) = organisestruc([chain]; struc_name=struc_name, model_number=model_number)\n+organisestruc(res::AbstractResidue; struc_name::AbstractString=\"\", model_number::Int=1) = organisestruc(AbstractResidue[res]; struc_name=struc_name, model_number=model_number)\n+organisestruc(atom::AbstractAtom; struc_name::AbstractString=\"\", model_number::Int=1) = organisestruc(AbstractAtom[atom]; struc_name=struc_name, model_number=model_number)\n+\n+\n+\"\"\"Determines if an `AbstractAtom` is a non-hetero atom, i.e. came from an ATOM\n+record.\"\"\"\n+stdatomselector(atom::AbstractAtom) = !ishetatom(atom)\n+\n+\"\"\"Determines if an `AbstractAtom` is a hetero atom, i.e. came from a HETATM\n+record.\"\"\"\n+hetatomselector(atom::AbstractAtom) = ishetatom(atom)\n+\n+\"\"\"Determines if an `AbstractAtom` has its atom name in the given `Array`.\"\"\"\n+atomnameselector(atom::AbstractAtom, atom_names::Array{AbstractString,1}) = getatomname(atom) in atom_names\n+\n+\"\"\"`Array` of C-alpha atom names.\"\"\"\n+const calpha_atom_names = AbstractString[\"CA\"]\n+\n+\"\"\"Determines if an `AbstractAtom` is not a hetero-atom and corresponds to a\n+C-alpha atom.\"\"\"\n+calphaselector(atom::AbstractAtom) = stdatomselector(atom) && atomnameselector(atom, calpha_atom_names)\n+\n+\"\"\"`Array` of protein backbone atom names.\"\"\"\n+const backbone_atom_names = AbstractString[\"CA\", \"N\", \"C\"]\n+\n+\"\"\"Determines if an `AbstractAtom` is not a hetero-atom and corresponds to a\n+protein backbone atom.\"\"\"\n+backboneselector(atom::AbstractAtom) = stdatomselector(atom) && atomnameselector(atom, backbone_atom_names)\n+\n+\"\"\"`Array` of protein standard heavy atom names.\"\"\"\n+const heavy_atom_names = AbstractString[\"C\", \"CA\", \"CB\", \"CD\", \"CD1\", \"CD2\",\n+                    \"CE\", \"CE1\", \"CE2\", \"CE3\", \"CG\", \"CG1\", \"CG2\", \"CH2\", \"CZ\",\n+                    \"CZ2\", \"CZ3\", \"N\", \"ND1\", \"ND2\", \"NE\", \"NE1\", \"NE2\", \"NH1\",\n+                    \"NH2\", \"NZ\", \"O\", \"OD1\", \"OD2\", \"OE1\", \"OE2\", \"OG\", \"OG1\",\n+                    \"OH\", \"SG\"]\n+\n+\"\"\"Determines if an `AbstractAtom` is not a hetero-atom and corresponds to a\n+standard protein heavy atom.\"\"\"\n+heavyatomselector(atom::AbstractAtom) = stdatomselector(atom) && atomnameselector(atom, heavy_atom_names)\n+\n+\"\"\"Determines if an `AbstractResidue` or `AbstractAtom` has its resiudue name in\n+the given `Array`.\"\"\"\n+resnameselector(element::Union{AbstractResidue, AbstractAtom}, res_names::Array{AbstractString,1}) = getresname(element) in res_names\n+\n+# Any more?\n+\"\"\"`Array` of residue names corresponding to water.\"\"\"\n+const water_res_names = AbstractString[\"HOH\"]\n+\n+\"\"\"Determines if an `AbstractResidue` or `AbstractAtom` represents a water\n+molecule.\"\"\"\n+waterselector(element::Union{AbstractResidue, AbstractAtom}) = resnameselector(element, water_res_names)\n+\n+\"\"\"Determines if an `AbstractResidue` represents a standard protein residue,\n+i.e. consists of ATOM records.\"\"\"\n+stdresselector(res::AbstractResidue) = !ishetres(res)\n+\n+\"\"\"Determines if an `AbstractResidue` represents a hetero molecule, i.e.\n+consists of HETATM records.\"\"\"\n+hetresselector(res::AbstractResidue) = ishetres(res)\n+\n+\"\"\"Determines whether an `AbstractAtom` or `AbstractResidue` is disordered, i.e.\n+has multiple locations in the case of atoms or multiple residue names (point\n+mutants) in the case of residues.\"\"\"\n+disorderselector(atom::AbstractAtom) = typeof(atom) == DisorderedAtom\n+disorderselector(res::AbstractResidue) = typeof(res) == DisorderedResidue\n+\n+# Either the element is H or the element field is empty, the atom name contains an H and there are no letters in the atom name before H\n+# For example atom names 1H and H1 would be hydrogens but NH1 would not\n+\"\"\"Determines if an `AbstractAtom` represents hydrogen. Uses the element field\n+where possible, otherwise uses the atom name.\"\"\"\n+hydrogenselector(atom::AbstractAtom) = getelement(atom) == \"H\" || (getelement(atom) == \"\" && 'H' in getatomname(atom) && !ismatch(r\"[a-zA-Z]\", getatomname(atom)[:findfirst(getatomname(atom), 'H')-1]))\n+\n+\n+function show(io::IO, struc::Structure)\n+    model = struc[1]\n+    println(io, \"Name                 -  \", getstrucname(struc))\n+    println(io, \"No models            -  \", countmodels(struc))","path":"src/struc/model.jl","position":765,"original_position":765,"commit_id":"d8c45bd293f4a4adbad2ba0e63418b8e849a1c06","original_commit_id":"d8c45bd293f4a4adbad2ba0e63418b8e849a1c06","user":{"login":"Blahah","id":836040,"avatar_url":"https://avatars.githubusercontent.com/u/836040?v=3","gravatar_id":"","url":"https://api.github.com/users/Blahah","html_url":"https://github.com/Blahah","followers_url":"https://api.github.com/users/Blahah/followers","following_url":"https://api.github.com/users/Blahah/following{/other_user}","gists_url":"https://api.github.com/users/Blahah/gists{/gist_id}","starred_url":"https://api.github.com/users/Blahah/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Blahah/subscriptions","organizations_url":"https://api.github.com/users/Blahah/orgs","repos_url":"https://api.github.com/users/Blahah/repos","events_url":"https://api.github.com/users/Blahah/events{/privacy}","received_events_url":"https://api.github.com/users/Blahah/received_events","type":"User","site_admin":false},"body":"Since there is plenty of whitespace after, how about simply `Number of models`","created_at":"2016-02-06T18:59:36Z","updated_at":"2016-02-06T18:59:36Z","html_url":"https://github.com/BioJulia/Bio.jl/pull/131#discussion_r52105893","pull_request_url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131","_links":{"self":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/comments/52105893"},"html":{"href":"https://github.com/BioJulia/Bio.jl/pull/131#discussion_r52105893"},"pull_request":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131"}}},"pull_request":{"url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131","id":58524079,"html_url":"https://github.com/BioJulia/Bio.jl/pull/131","diff_url":"https://github.com/BioJulia/Bio.jl/pull/131.diff","patch_url":"https://github.com/BioJulia/Bio.jl/pull/131.patch","issue_url":"https://api.github.com/repos/BioJulia/Bio.jl/issues/131","number":131,"state":"open","locked":false,"title":"Struc module - framework for parsing PDB files and dealing with protein structures","user":{"login":"jgreener64","id":6620652,"avatar_url":"https://avatars.githubusercontent.com/u/6620652?v=3","gravatar_id":"","url":"https://api.github.com/users/jgreener64","html_url":"https://github.com/jgreener64","followers_url":"https://api.github.com/users/jgreener64/followers","following_url":"https://api.github.com/users/jgreener64/following{/other_user}","gists_url":"https://api.github.com/users/jgreener64/gists{/gist_id}","starred_url":"https://api.github.com/users/jgreener64/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgreener64/subscriptions","organizations_url":"https://api.github.com/users/jgreener64/orgs","repos_url":"https://api.github.com/users/jgreener64/repos","events_url":"https://api.github.com/users/jgreener64/events{/privacy}","received_events_url":"https://api.github.com/users/jgreener64/received_events","type":"User","site_admin":false},"body":"This is a work-in-progress PR containing a potential new module for base BioJulia - Struc, which deals with PDB files and protein structures. See the [notebook](https://github.com/jgreener64/Bio.jl/blob/strucmin/doc/struc.ipynb) in docs for an overview; the code also has comments.\r\n\r\nWould be interested to hear thoughts on the overall structure of the module - is it doing the right type of things in the right way? The overall approach is heavily based on the Biopython PDB parser.\r\n\r\nI am currently working on improving parts of the code, particularly the `organise` and `read` functions. Speed-wise the collect functions and iterations are fast but `organise` and `read` take longer. Also working on writing tests.\r\n\r\nThanks,\r\nJoe","created_at":"2016-02-06T16:43:43Z","updated_at":"2016-02-06T18:59:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"76a7f443bb8b2b098bb7b91b4e523a990e8b10ca","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131/commits","review_comments_url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131/comments","review_comment_url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/comments{/number}","comments_url":"https://api.github.com/repos/BioJulia/Bio.jl/issues/131/comments","statuses_url":"https://api.github.com/repos/BioJulia/Bio.jl/statuses/d8c45bd293f4a4adbad2ba0e63418b8e849a1c06","head":{"label":"jgreener64:strucmin","ref":"strucmin","sha":"d8c45bd293f4a4adbad2ba0e63418b8e849a1c06","user":{"login":"jgreener64","id":6620652,"avatar_url":"https://avatars.githubusercontent.com/u/6620652?v=3","gravatar_id":"","url":"https://api.github.com/users/jgreener64","html_url":"https://github.com/jgreener64","followers_url":"https://api.github.com/users/jgreener64/followers","following_url":"https://api.github.com/users/jgreener64/following{/other_user}","gists_url":"https://api.github.com/users/jgreener64/gists{/gist_id}","starred_url":"https://api.github.com/users/jgreener64/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgreener64/subscriptions","organizations_url":"https://api.github.com/users/jgreener64/orgs","repos_url":"https://api.github.com/users/jgreener64/repos","events_url":"https://api.github.com/users/jgreener64/events{/privacy}","received_events_url":"https://api.github.com/users/jgreener64/received_events","type":"User","site_admin":false},"repo":{"id":49076779,"name":"Bio.jl","full_name":"jgreener64/Bio.jl","owner":{"login":"jgreener64","id":6620652,"avatar_url":"https://avatars.githubusercontent.com/u/6620652?v=3","gravatar_id":"","url":"https://api.github.com/users/jgreener64","html_url":"https://github.com/jgreener64","followers_url":"https://api.github.com/users/jgreener64/followers","following_url":"https://api.github.com/users/jgreener64/following{/other_user}","gists_url":"https://api.github.com/users/jgreener64/gists{/gist_id}","starred_url":"https://api.github.com/users/jgreener64/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgreener64/subscriptions","organizations_url":"https://api.github.com/users/jgreener64/orgs","repos_url":"https://api.github.com/users/jgreener64/repos","events_url":"https://api.github.com/users/jgreener64/events{/privacy}","received_events_url":"https://api.github.com/users/jgreener64/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jgreener64/Bio.jl","description":"Bioinformatics and Computational Biology Infrastructure for Julia","fork":true,"url":"https://api.github.com/repos/jgreener64/Bio.jl","forks_url":"https://api.github.com/repos/jgreener64/Bio.jl/forks","keys_url":"https://api.github.com/repos/jgreener64/Bio.jl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jgreener64/Bio.jl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jgreener64/Bio.jl/teams","hooks_url":"https://api.github.com/repos/jgreener64/Bio.jl/hooks","issue_events_url":"https://api.github.com/repos/jgreener64/Bio.jl/issues/events{/number}","events_url":"https://api.github.com/repos/jgreener64/Bio.jl/events","assignees_url":"https://api.github.com/repos/jgreener64/Bio.jl/assignees{/user}","branches_url":"https://api.github.com/repos/jgreener64/Bio.jl/branches{/branch}","tags_url":"https://api.github.com/repos/jgreener64/Bio.jl/tags","blobs_url":"https://api.github.com/repos/jgreener64/Bio.jl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jgreener64/Bio.jl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jgreener64/Bio.jl/git/refs{/sha}","trees_url":"https://api.github.com/repos/jgreener64/Bio.jl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jgreener64/Bio.jl/statuses/{sha}","languages_url":"https://api.github.com/repos/jgreener64/Bio.jl/languages","stargazers_url":"https://api.github.com/repos/jgreener64/Bio.jl/stargazers","contributors_url":"https://api.github.com/repos/jgreener64/Bio.jl/contributors","subscribers_url":"https://api.github.com/repos/jgreener64/Bio.jl/subscribers","subscription_url":"https://api.github.com/repos/jgreener64/Bio.jl/subscription","commits_url":"https://api.github.com/repos/jgreener64/Bio.jl/commits{/sha}","git_commits_url":"https://api.github.com/repos/jgreener64/Bio.jl/git/commits{/sha}","comments_url":"https://api.github.com/repos/jgreener64/Bio.jl/comments{/number}","issue_comment_url":"https://api.github.com/repos/jgreener64/Bio.jl/issues/comments{/number}","contents_url":"https://api.github.com/repos/jgreener64/Bio.jl/contents/{+path}","compare_url":"https://api.github.com/repos/jgreener64/Bio.jl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jgreener64/Bio.jl/merges","archive_url":"https://api.github.com/repos/jgreener64/Bio.jl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jgreener64/Bio.jl/downloads","issues_url":"https://api.github.com/repos/jgreener64/Bio.jl/issues{/number}","pulls_url":"https://api.github.com/repos/jgreener64/Bio.jl/pulls{/number}","milestones_url":"https://api.github.com/repos/jgreener64/Bio.jl/milestones{/number}","notifications_url":"https://api.github.com/repos/jgreener64/Bio.jl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jgreener64/Bio.jl/labels{/name}","releases_url":"https://api.github.com/repos/jgreener64/Bio.jl/releases{/id}","deployments_url":"https://api.github.com/repos/jgreener64/Bio.jl/deployments","created_at":"2016-01-05T16:08:11Z","updated_at":"2016-01-22T17:31:38Z","pushed_at":"2016-02-06T16:20:43Z","git_url":"git://github.com/jgreener64/Bio.jl.git","ssh_url":"git@github.com:jgreener64/Bio.jl.git","clone_url":"https://github.com/jgreener64/Bio.jl.git","svn_url":"https://github.com/jgreener64/Bio.jl","homepage":"http://biojulia.github.io/Bio.jl/","size":1259,"stargazers_count":0,"watchers_count":0,"language":"Julia","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"BioJulia:master","ref":"master","sha":"ebf7062e38c6fc9ca8c5426679dddaca205b59b1","user":{"login":"BioJulia","id":6486271,"avatar_url":"https://avatars.githubusercontent.com/u/6486271?v=3","gravatar_id":"","url":"https://api.github.com/users/BioJulia","html_url":"https://github.com/BioJulia","followers_url":"https://api.github.com/users/BioJulia/followers","following_url":"https://api.github.com/users/BioJulia/following{/other_user}","gists_url":"https://api.github.com/users/BioJulia/gists{/gist_id}","starred_url":"https://api.github.com/users/BioJulia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BioJulia/subscriptions","organizations_url":"https://api.github.com/users/BioJulia/orgs","repos_url":"https://api.github.com/users/BioJulia/repos","events_url":"https://api.github.com/users/BioJulia/events{/privacy}","received_events_url":"https://api.github.com/users/BioJulia/received_events","type":"Organization","site_admin":false},"repo":{"id":16187538,"name":"Bio.jl","full_name":"BioJulia/Bio.jl","owner":{"login":"BioJulia","id":6486271,"avatar_url":"https://avatars.githubusercontent.com/u/6486271?v=3","gravatar_id":"","url":"https://api.github.com/users/BioJulia","html_url":"https://github.com/BioJulia","followers_url":"https://api.github.com/users/BioJulia/followers","following_url":"https://api.github.com/users/BioJulia/following{/other_user}","gists_url":"https://api.github.com/users/BioJulia/gists{/gist_id}","starred_url":"https://api.github.com/users/BioJulia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BioJulia/subscriptions","organizations_url":"https://api.github.com/users/BioJulia/orgs","repos_url":"https://api.github.com/users/BioJulia/repos","events_url":"https://api.github.com/users/BioJulia/events{/privacy}","received_events_url":"https://api.github.com/users/BioJulia/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/BioJulia/Bio.jl","description":"Bioinformatics and Computational Biology Infrastructure for Julia","fork":false,"url":"https://api.github.com/repos/BioJulia/Bio.jl","forks_url":"https://api.github.com/repos/BioJulia/Bio.jl/forks","keys_url":"https://api.github.com/repos/BioJulia/Bio.jl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/BioJulia/Bio.jl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/BioJulia/Bio.jl/teams","hooks_url":"https://api.github.com/repos/BioJulia/Bio.jl/hooks","issue_events_url":"https://api.github.com/repos/BioJulia/Bio.jl/issues/events{/number}","events_url":"https://api.github.com/repos/BioJulia/Bio.jl/events","assignees_url":"https://api.github.com/repos/BioJulia/Bio.jl/assignees{/user}","branches_url":"https://api.github.com/repos/BioJulia/Bio.jl/branches{/branch}","tags_url":"https://api.github.com/repos/BioJulia/Bio.jl/tags","blobs_url":"https://api.github.com/repos/BioJulia/Bio.jl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/BioJulia/Bio.jl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/BioJulia/Bio.jl/git/refs{/sha}","trees_url":"https://api.github.com/repos/BioJulia/Bio.jl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/BioJulia/Bio.jl/statuses/{sha}","languages_url":"https://api.github.com/repos/BioJulia/Bio.jl/languages","stargazers_url":"https://api.github.com/repos/BioJulia/Bio.jl/stargazers","contributors_url":"https://api.github.com/repos/BioJulia/Bio.jl/contributors","subscribers_url":"https://api.github.com/repos/BioJulia/Bio.jl/subscribers","subscription_url":"https://api.github.com/repos/BioJulia/Bio.jl/subscription","commits_url":"https://api.github.com/repos/BioJulia/Bio.jl/commits{/sha}","git_commits_url":"https://api.github.com/repos/BioJulia/Bio.jl/git/commits{/sha}","comments_url":"https://api.github.com/repos/BioJulia/Bio.jl/comments{/number}","issue_comment_url":"https://api.github.com/repos/BioJulia/Bio.jl/issues/comments{/number}","contents_url":"https://api.github.com/repos/BioJulia/Bio.jl/contents/{+path}","compare_url":"https://api.github.com/repos/BioJulia/Bio.jl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/BioJulia/Bio.jl/merges","archive_url":"https://api.github.com/repos/BioJulia/Bio.jl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/BioJulia/Bio.jl/downloads","issues_url":"https://api.github.com/repos/BioJulia/Bio.jl/issues{/number}","pulls_url":"https://api.github.com/repos/BioJulia/Bio.jl/pulls{/number}","milestones_url":"https://api.github.com/repos/BioJulia/Bio.jl/milestones{/number}","notifications_url":"https://api.github.com/repos/BioJulia/Bio.jl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/BioJulia/Bio.jl/labels{/name}","releases_url":"https://api.github.com/repos/BioJulia/Bio.jl/releases{/id}","deployments_url":"https://api.github.com/repos/BioJulia/Bio.jl/deployments","created_at":"2014-01-23T22:20:21Z","updated_at":"2016-01-26T18:17:15Z","pushed_at":"2016-02-06T16:43:43Z","git_url":"git://github.com/BioJulia/Bio.jl.git","ssh_url":"git@github.com:BioJulia/Bio.jl.git","clone_url":"https://github.com/BioJulia/Bio.jl.git","svn_url":"https://github.com/BioJulia/Bio.jl","homepage":"http://biojulia.github.io/Bio.jl/","size":1707,"stargazers_count":88,"watchers_count":88,"language":"Julia","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":31,"mirror_url":null,"open_issues_count":32,"forks":31,"open_issues":32,"watchers":88,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131"},"html":{"href":"https://github.com/BioJulia/Bio.jl/pull/131"},"issue":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/issues/131"},"comments":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/issues/131/comments"},"review_comments":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131/comments"},"review_comment":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/pulls/131/commits"},"statuses":{"href":"https://api.github.com/repos/BioJulia/Bio.jl/statuses/d8c45bd293f4a4adbad2ba0e63418b8e849a1c06"}}}}