Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 12 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"Bash(python -m build:*)",
"Bash(python:*)",
"Bash(git rm:*)",
"Bash(make:*)"
],
"deny": [],
"ask": []
}
}
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v4
with:
fetch-depth: 1

Expand Down
91 changes: 91 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Build and Deploy Documentation

on:
push:
branches:
- master
- oxbow # Build docs for development branch as well
paths:
- 'docs/**'
- 'coolbox/**'
- '.github/workflows/docs.yml'
pull_request:
branches:
- master
paths:
- 'docs/**'
- 'coolbox/**'
workflow_dispatch: # Allow manual trigger

permissions:
contents: write # Required for pushing to gh-pages branch

jobs:
build-and-deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch full history for version info

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y pandoc

- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
# Install project with documentation dependencies
pip install -e ".[doc]"

- name: Build documentation
run: |
cd docs
# Clean old build artifacts if any
rm -rf build/
# Build documentation with Sphinx
sphinx-build -b html source build/html -W --keep-going
# Create .nojekyll file to tell GitHub Pages not to ignore underscore-prefixed directories
touch build/html/.nojekyll
env:
SPHINXOPTS: "-W --keep-going"

- name: Check documentation
run: |
cd docs/build/html
echo "Documentation build completed successfully!"
echo "Generated files:"
ls -lh
echo ""
echo "Total size:"
du -sh .

# Only deploy when pushing to master branch
- name: Deploy to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build/html
publish_branch: gh-pages
force_orphan: true # Create orphan gh-pages branch without history
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'
commit_message: 'Deploy documentation from ${{ github.sha }}'

- name: Upload documentation artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: documentation
path: docs/build/html
retention-days: 7
4 changes: 2 additions & 2 deletions .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
max-parallel: 5

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: 3.9
- name: Update Conda
Expand Down
16 changes: 9 additions & 7 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

name: Upload Python Package

Expand All @@ -14,19 +14,21 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
pip install build twine
- name: Build package
run: |
python -m build
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ instance/

# Sphinx documentation
docs/build/
# Sphinx generated HTML files in docs root (old structure)
# These should now be built by CI/CD and not committed
docs/*.html
docs/*.js
docs/_sources/
docs/_static/
docs/_images/
docs/_gallery/
docs/objects.inv

# PyBuilder
target/
Expand Down
2 changes: 1 addition & 1 deletion coolbox/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.3.9'
__version__ = '0.4.0'
12 changes: 12 additions & 0 deletions coolbox/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env python

from coolbox.cli import CLI
import fire


def main():
fire.Fire(CLI)


if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion coolbox/core/browser/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ def preload_imgs(self, directions):
Preloading images to self.fig_cache.

Can load image in one of 4 directions:
left, right, zoom-in, zoom-out

- left, right, zoom-in, zoom-out

or load all directions.
"""

Expand Down
7 changes: 7 additions & 0 deletions coolbox/core/coverage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@
HistCoverage = track_to_coverage(Hist)
BigWigCoverage = track_to_coverage(BigWig)
BedGraphCoverage = track_to_coverage(BedGraph)

__all__ = [
"HighLights", "HighLightsFromFile", "Vlines", "VlinesFromFile", "HLines",
"TADCoverage", "ArcsCoverage", "PairsCoverage", "BEDPECoverage", "HiCPeaksCoverage",
"HistCoverage", "BigWigCoverage", "BedGraphCoverage",
"track_to_coverage",
]
9 changes: 8 additions & 1 deletion coolbox/core/coverage/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ def check_track_type(self, allow):
)
raise ValueError(msg)

def fetch_data(self, gr: GenomeRange, **kwargs):
raise NotImplementedError


class CoverageStack(object):
"""
Expand Down Expand Up @@ -189,14 +192,18 @@ def plot(self, ax, gr: GenomeRange, **kwargs):
self.track_instance.properties['height'] = self.track.properties['height']
self.track_instance.plot(ax, gr, **kwargs)

def fetch_data(self, gr: GenomeRange, **kwargs):
return self.track_instance.fetch_data(gr, **kwargs)

# TODO , other track methods
cov_class = type(
track_class.__name__ + "Coverage",
(Coverage,),
{
"__init__": init,
"__doc__": track_class.__doc__,
"plot": plot
"plot": plot,
"fetch_data": fetch_data
}
)

Expand Down
66 changes: 24 additions & 42 deletions coolbox/core/coverage/highlights.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from coolbox.utilities import (
opener, ReadBed,
Interval, IntervalTree,
rgb2hex,
get_logger, GenomeRange,
to_gr
)
from coolbox.utilities.reader.tab import get_indexed_tab_reader
from .base import Coverage

log = get_logger(__name__)
Expand All @@ -13,24 +12,10 @@
class _Highlights(object):
DEFAULT_COLOR = '#ff5d0f'

def fetch_data(self, gr: GenomeRange, **kwargs):
gr = to_gr(gr)
if gr.chrom not in list(self.interval_tree):
gr.change_chrom_names()

return [
(region.begin, region.end, region.data)
for region in sorted(
self.interval_tree[gr.chrom][gr.start - 10000 : gr.end + 10000]
)
]

def plot(self, ax, gr: GenomeRange, **kwargs):

regions = self.fetch_data(gr, **kwargs)

for (start, end, color) in regions:
if self.properties['color'] != 'bed_rgb':
if self.properties['color'] != 'rgb':
color = self.properties['color']
if type(color) is not str:
color = rgb2hex(*color)
Expand All @@ -55,10 +40,11 @@ class HighLightsFromFile(Coverage, _Highlights):
----------
file_ : str
Path to the file.
Contains columns: chrom, start, end, color

color : str, optional
High light region color,
use 'bed_rgb' for specify color from the file, default 'bed_rgb'.
use 'rgb' for specify color from the file, default 'rgb'.

alpha : float, optional
High light region alpha value, default 0.1.
Expand All @@ -81,11 +67,12 @@ class HighLightsFromFile(Coverage, _Highlights):
name : str, optional
The name of thr Coverage.
"""
FIELDS = ['chrom', 'start', 'end', 'color']

def __init__(self, file_, **kwargs):
properties_dict = {
"file": file_,
"color": "bed_rgb",
"color": "rgb",
"alpha": 0.1,
"border_line": False,
"border_line_style": "dashed",
Expand All @@ -95,30 +82,13 @@ def __init__(self, file_, **kwargs):
}
properties_dict.update(kwargs)
super().__init__(properties_dict)
self.interval_tree = self.__process_bed()

def __process_bed(self):
bed = ReadBed(opener(self.properties['file']))

if self.properties['color'] == 'bed_rgb' and bed.file_type not in ['bed12', 'bed9']:
log.warning("*WARNING* Color set to 'bed_rgb', but bed file does not have the rgb field. The color has "
"been set to {}".format(HighLights.DEFAULT_COLOR))
self.properties['color'] = HighLights.DEFAULT_COLOR

interval_tree = {}

for intval in bed:
self.reader = get_indexed_tab_reader(
self.properties['file'],
columns=HighLightsFromFile.FIELDS)

if intval.chromosome not in interval_tree:
interval_tree[intval.chromosome] = IntervalTree()

if self.properties['color'] == 'bed_rgb':
color = intval.rgb
else:
color = self.properties['color']
interval_tree[intval.chromosome].add(Interval(intval.start, intval.end, color))

return interval_tree
def fetch_data(self, gr: GenomeRange, **kwargs):
df = self.reader.query_var_chr(gr, **kwargs)
return df[['start', 'end', 'color']].values.tolist()


class HighLights(Coverage, _Highlights):
Expand Down Expand Up @@ -195,3 +165,15 @@ def __intervaltree_from_list(self, region_list):
itree.setdefault(chr_, IntervalTree())
itree[chr_][grange.start:grange.end + 1] = grange
return itree

def fetch_data(self, gr: GenomeRange, **kwargs):
gr = to_gr(gr)
if gr.chrom not in list(self.interval_tree):
gr = gr.change_chrom_names()

return [
(region.begin, region.end, region.data)
for region in sorted(
self.interval_tree[gr.chrom][gr.start - 10000 : gr.end + 10000]
)
]
Loading