Skip to content

Commit

Permalink
Merge branch 'release/v2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
CiaranOMara committed Apr 20, 2023
2 parents 483a38e + c7f2cb6 commit b99efce
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 43 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/CompatHelper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@ on:
schedule:
- cron: 0 0 * * *
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
CompatHelper:
runs-on: ubuntu-latest
steps:
- name: Check if Julia is already available in the PATH
id: julia_in_path
run: which julia
continue-on-error: true
- name: Install Julia, but only if it is not already available in the PATH
uses: julia-actions/setup-julia@v1
with:
version: '1'
arch: ${{ runner.arch }}
if: steps.julia_in_path.outcome != 'success'
- name: "Add the General registry via Git"
run: |
import Pkg
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ on:

jobs:
Documenter:
permissions:
contents: write
name: Documentation
runs-on: ubuntu-latest
steps:
Expand Down
18 changes: 16 additions & 2 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
name: TagBot

on:
issue_comment:
types:
- created
workflow_dispatch:

inputs:
lookback:
default: 3
permissions:
actions: read
checks: read
contents: write
deployments: read
issues: read
discussions: read
packages: read
pages: read
pull-requests: read
repository-projects: read
security-events: read
statuses: read
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
Expand Down
25 changes: 8 additions & 17 deletions .github/workflows/UnitTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,18 @@ jobs:
test:
name: Julia ${{ matrix.julia-version }} - ${{ matrix.os }} - ${{ matrix.julia-arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
continue-on-error: ${{ matrix.julia-version == 'nightly' }}
strategy:
fail-fast: false
matrix:
julia-version:
- '1.0' # LTS
- '1.6' # LTS
- '1'
julia-arch: [x64, x86]
os: [ubuntu-latest, windows-latest, macOS-latest]
exclude:
- os: macOS-latest
julia-arch: x86
experimental: [false]
include:
- julia-version: nightly
julia-arch: x64
os: ubuntu-latest
experimental: true
- 'nightly'
julia-arch:
- x64
os: [ubuntu-latest]

steps:
- name: Checkout Repository
Expand All @@ -34,15 +28,12 @@ jobs:
uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.julia-version }}
arch: ${{ matrix.julia-arch }}
- name: Run Tests
uses: julia-actions/julia-runtest@v1
- name: Create CodeCov
uses: julia-actions/julia-processcoverage@v1
- name: Upload CodeCov
uses: codecov/codecov-action@v2
with:
file: lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
files: lcov.info
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "GenomicFeatures"
uuid = "899a7d2d-5c61-547b-bef9-6698a8d05446"
authors = ["Kenta Sato <[email protected]>", "Ben J. Ward <benjward@protonmail.com>", "Ciarán O’Mara <[email protected]>"]
version = "2.0.5"
authors = ["Kenta Sato <[email protected]>", "Sabrina J. Ward <sabrinajward@protonmail.com>", "Ciarán O’Mara <[email protected]>"]
version = "2.1.0"

[deps]
BioGenerics = "47718e42-2ac5-11e9-14af-e5595289c2ea"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# <img src="./sticker.svg" width="30%" align="right" /> GenomicFeatures

[![latest release](https://img.shields.io/github/release/BioJulia/GenomicFeatures.jl.svg)](https://github.com/BioJulia/GenomicFeatures.jl/releases/latest)
[![DOI](https://zenodo.org/badge/94160625.svg)](https://zenodo.org/badge/latestdoi/94160625)
[![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/BioJulia/GenomicFeatures.jl/blob/master/LICENSE)
[![Stable documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://biojulia.github.io/GenomicFeatures.jl/stable)
[![Latest documentation](https://img.shields.io/badge/docs-dev-blue.svg)](https://biojulia.github.io/GenomicFeatures.jl/dev/)
Expand Down Expand Up @@ -69,4 +70,4 @@ Your logo will show up here with a link to your website.

## Questions?

If you have a question about contributing or using BioJulia software, come on over and chat to us on [the Julia Slack workspace](https://julialang.org/slack/), or you can try the [Bio category of the Julia discourse site](https://discourse.julialang.org/c/domain/bio).
If you have a question about contributing or using BioJulia software, come on over and chat to us on [the Julia Slack workspace](https://julialang.slack.com/channels/biology), or you can try the [Bio category of the Julia discourse site](https://discourse.julialang.org/c/domain/bio).
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ Your logo will show up here with a link to your website.

## Questions?

If you have a question about contributing or using BioJulia software, come on over and chat to us on [the Julia Slack workspace](https://julialang.org/slack/), or you can try the [Bio category of the Julia discourse site](https://discourse.julialang.org/c/domain/bio).
If you have a question about contributing or using BioJulia software, come on over and chat to us on [the Julia Slack workspace](https://julialang.slack.com/channels/biology), or you can try the [Bio category of the Julia discourse site](https://discourse.julialang.org/c/domain/bio).
23 changes: 22 additions & 1 deletion src/GenomicFeatures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ export
IntervalCollection,
eachoverlap,
coverage,
hasintersection,

isfilled,
hasseqname,
hasleftposition,
hasrightposition
hasrightposition,

span

import BioGenerics: BioGenerics, seqname, leftposition, rightposition, isoverlapping, isfilled, hasseqname, hasleftposition, hasrightposition, metadata
import DataStructures
Expand All @@ -38,4 +41,22 @@ include("queue.jl")
include("overlap.jl")
include("coverage.jl")

"""
span(interval::Interval)::Int
Get the span of `interval`.
"""
function span(interval::Interval)
return length(leftposition(interval):rightposition(interval))
end

"""
volume(interval::Interval)
Get the product of the `interval`'s span and metadata.
"""
function volume(interval::Interval)
return span(interval) * GenomicFeatures.metadata(interval)
end

end # module
24 changes: 10 additions & 14 deletions src/coverage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ function coverage(stream, seqname_isless::Function=isless)
error("Intervals must be sorted to compute coverage.")
end

if !isempty(lasts) && lasts[1] < first(interval)
if !isempty(lasts) && lasts[1] < leftposition(interval)
pos = DataStructures.heappop!(lasts)
if first(interval) == pos + 1
DataStructures.heappush!(lasts, last(interval))
if leftposition(interval) == pos + 1
DataStructures.heappush!(lasts, rightposition(interval))
if stream_next === nothing
break
end
last_interval_first = first(interval)
last_interval_first = leftposition(interval)
interval, stream_state = stream_next
stream_next = iterate(stream, stream_state)
elseif pos == coverage_first - 1
Expand All @@ -90,23 +90,23 @@ function coverage(stream, seqname_isless::Function=isless)
end
else
if coverage_first == 0
coverage_first = first(interval)
coverage_first = leftposition(interval)
current_coverage = 1
elseif coverage_first == first(interval)
elseif coverage_first == leftposition(interval)
current_coverage += 1
else
if current_coverage > 0
push!(cov, Interval{UInt32}(coverage_seqname, coverage_first, first(interval) - 1, STRAND_BOTH, current_coverage))
push!(cov, Interval{UInt32}(coverage_seqname, coverage_first, leftposition(interval) - 1, STRAND_BOTH, current_coverage))
end
current_coverage += 1
coverage_first = first(interval)
coverage_first = leftposition(interval)
end

DataStructures.heappush!(lasts, last(interval))
DataStructures.heappush!(lasts, rightposition(interval))
if stream_next === nothing
break
end
last_interval_first = first(interval)
last_interval_first = leftposition(interval)
interval, stream_state = stream_next
stream_next = iterate(stream, stream_state)
end
Expand All @@ -117,10 +117,6 @@ function coverage(stream, seqname_isless::Function=isless)
return cov
end

function coverage(ic::IntervalCollection)
return coverage(ic, isless)
end

# Helper function for coverage. Process remaining interval end points after
# all intervals have been read.
function coverage_process_lasts_heap!(cov::IntervalCollection{UInt32}, current_coverage, coverage_seqname, coverage_first, lasts)
Expand Down
8 changes: 4 additions & 4 deletions src/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ end
IntervalTrees.first(i::Interval) = leftposition(i)
IntervalTrees.last(i::Interval) = rightposition(i)

function Base.isless(a::Interval{T}, b::Interval{T}, seqname_isless::Function=isless) where T
function Base.isless(a::Interval, b::Interval, seqname_isless::Function=isless)
if seqname(a) != seqname(b)
return seqname_isless(seqname(a), seqname(b))::Bool
end
Expand All @@ -104,7 +104,7 @@ Check if two intervals are well ordered.
Intervals are considered well ordered if seqname(a) <= seqname(b) and leftposition(a) <= leftposition(b).
"""
function isordered(a::Interval{T}, b::Interval{T}, seqname_isless::Function=isless) where T
function isordered(a::Interval, b::Interval, seqname_isless::Function=isless)
if seqname(a) != seqname(b)
return seqname_isless(seqname(a), seqname(b))::Bool
end
Expand All @@ -119,11 +119,11 @@ end
"""
Return true if interval `a` entirely precedes `b`.
"""
function precedes(a::Interval{T}, b::Interval{T}, seqname_isless::Function=isless) where T
function precedes(a::Interval, b::Interval, seqname_isless::Function=isless)
return (rightposition(a) < leftposition(b) && seqname(a) == seqname(b)) || seqname_isless(seqname(a), seqname(b))::Bool
end

function Base.:(==)(a::Interval{T}, b::Interval{T}) where T
function Base.:(==)(a::Interval, b::Interval)
return seqname(a) == seqname(b) &&
leftposition(a) == leftposition(b) &&
rightposition(a) == rightposition(b) &&
Expand Down
35 changes: 34 additions & 1 deletion src/intervalcollection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function Base.eltype(::Type{IntervalCollection{T}}) where T
return Interval{T}
end

function Base.:(==)(a::IntervalCollection{T}, b::IntervalCollection{T}) where T
function Base.:(==)(a::IntervalCollection, b::IntervalCollection)
if length(a) != length(b)
return false
end
Expand Down Expand Up @@ -304,6 +304,10 @@ function eachoverlap(a::IntervalCollection{T}, query::Interval; filter::F = true
return ICTreeIntervalIntersectionIterator{F,T}(filter, ICTreeIntersection{T}(), ICTree{T}(), query)
end

function eachoverlap(query::Interval, b::IntervalCollection{T}; filter::F = true_cmp) where {F,T}
return eachoverlap(b, query; filter = filter)
end

function eachoverlap(a::IntervalCollection, b::IntervalCollection; filter = true_cmp)
seqnames = collect(AbstractString, keys(a.trees) keys(b.trees))
sort!(seqnames, lt = isless)
Expand Down Expand Up @@ -499,3 +503,32 @@ function Base.iterate(it::IntervalCollectionStreamIterator{F,S,T}, state = ()) w
end
end
end

"""
hasintersection(interval::Interval, col::IntervalCollection)::Bool
Query whether an `interval` has an intersection with `col`.
"""
function hasintersection(interval::Interval, col::IntervalCollection)

# Return early if chromosome is not in the interval collection.
if !haskey(col.trees, seqname(interval))
return false
end

# Setup intersection iterator.
iter = IntervalTrees.intersect(col.trees[seqname(interval)], (leftposition(interval), rightposition(interval)))

# Attempt first iteration.
if iterate(iter) === nothing
return false
end

# Intersection exists.
return true

end

function hasintersection(col::IntervalCollection)
return interval -> hasintersection(interval, col)
end
12 changes: 12 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ end
@test i1 == i2
@test i1 == Interval("chr2", 5692667:5701385, '+', "SOX11")
end

@test span(Interval("test", 1, 9)) == length(1:9)
@test GenomicFeatures.volume(Interval("test", 1, 9, '?', 2.5)) == length(1:9) * 2.5

end

@testset "IntervalCollection" begin
Expand Down Expand Up @@ -215,6 +219,14 @@ end
sort(simple_intersection(intervals_a, intervals_b))
@test sort(collect(eachoverlap(ic_a, ic_b, filter=(a,b) -> isodd(first(a))))) ==
sort(simple_intersection(intervals_a, intervals_b, filter=(a,b) -> isodd(first(a))))

# Check hasintersection method.
@test hasintersection(Interval("test", 1, 1), IntervalCollection([Interval("test", 1,2)])) == true
@test hasintersection(Interval("test", 1, 1), IntervalCollection([Interval("test", 2,2)])) == false

# Check hasintersection currying.
@test Interval("test", 1, 1) |> hasintersection(IntervalCollection([Interval("test", 1,2)])) == true
@test Interval("test", 1, 1) |> hasintersection(IntervalCollection([Interval("test", 2,2)])) == false
end

@testset "Show" begin
Expand Down

2 comments on commit b99efce

@CiaranOMara
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/81987

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v2.1.0 -m "<description of version>" b99efced1167938254f76db46b5813752e662ed1
git push origin v2.1.0

Please sign in to comment.