Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: CI

on:
push:
branches: [main, full-pipeline]
pull_request:
branches: [main, full-pipeline]
workflow_dispatch:

jobs:
# ---------------------------------------------------------------------
# Fast, no-network-deps check: every .py file parses with base Python
# (catches typos/syntax errors immediately, no package installs needed).
# ---------------------------------------------------------------------
syntax-check:
name: Python syntax check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Parse every .py file
run: |
set -e
status=0
while IFS= read -r -d '' f; do
python -m py_compile "$f" || status=1
done < <(find . -name "*.py" -not -path "./.git/*" -print0)
exit $status

# ---------------------------------------------------------------------
# Validate the Snakemake DAG for every variant/config combination,
# without needing RNAfold, the C++ binary, or trained models.
# ---------------------------------------------------------------------
snakemake-dry-run:
name: Snakemake DAG validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Snakemake
run: |
python -m pip install --upgrade pip
pip install snakemake "pulp<2.8"
- name: Dry-run every variant / config combination
run: |
set -e
snakemake --cores 1 -n
snakemake --cores 1 -n --config run_scoring=true
snakemake --cores 1 -n --config variant=broad_pam
snakemake --cores 1 -n --config variant=base_editor
- name: Reject an invalid variant
run: '! snakemake --cores 1 -n --config variant=bogus'

# ---------------------------------------------------------------------
# Real execution smoke test: installs the actual Python dependencies
# (numpy/pandas/biopython/ViennaRNA Python bindings -- NOT TensorFlow,
# NOT the C++ binary) and runs Steps 1-3 against the bundled demo FASTA.
# This is as far as this pipeline can run without the missing
# connection_to_matrix.cpp source and trained .h5 models (see README
# "Known Issues") -- those two gaps are the repository owner's to fill.
# ---------------------------------------------------------------------
smoke-test:
name: Steps 1-3 smoke test (demo FASTA)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install lightweight pipeline dependencies
run: |
python -m pip install --upgrade pip
pip install numpy pandas biopython ViennaRNA
pip install snakemake "pulp<2.8"
- name: Run steps 1-3 against the demo FASTA
run: snakemake --cores 1 Structure_file.csv Structure_Connection.fa Target_sequence_feature.csv
- name: Verify outputs
run: |
for f in Structure_file.csv Structure_Connection.fa Structure_Connection.csv Target_sequence_feature.csv; do
test -s "$f" || { echo "Missing or empty output: $f"; exit 1; }
done
- name: Upload smoke-test outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: dgd-smoke-test-outputs
path: |
*.csv
*.fa
logs/
retention-days: 7
42 changes: 42 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Byte-compiled / cache
__pycache__/
*.py[cod]
*$py.class

# Virtual environments
venv/
.venv/
env/

# Compiled C++ binary (built locally via `make`)
connection_to_matrix

# Trained model weights (large binaries; not tracked in git)
models/
*.h5

# Generated pipeline intermediate/output files (see PipelineFiles in DGD.py)
Structure_file.csv
Structure_Connection.fa
Structure_Connection.csv
Structure_Connection.out
Structure_Connection.outs
Structure_Cas9_out.txt
Structure_out.txt
Structure_basepairs.csv
spacer_scaffold_basepairs.csv
spacer_scaffold_feature.csv
Structural_annotation.csv
Feature_Data_Spacer_Scaffold.csv
Deep_learning_file.csv
DGD.csv
DGDVar.csv
DGDBE.csv
results/

# Snakemake
.snakemake/

# OS / editor cruft
.DS_Store
*.swp
15 changes: 13 additions & 2 deletions Base-Editors/DGDbaseeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,18 @@ def _count_gc_content(sequence: str, partner_index: list) -> int:

def _calculate_free_energy(dimers: list) -> float:
stacking = return_stacking_model()
return sum(stacking[dimers[i]][dimers[i+1][::-1]] for i in range(0, len(dimers), 2))
# See KNOWN ISSUE note in stacking_model.return_stacking_model(): the
# table only covers 4 of 16 dinucleotide contexts, and `dimers` is not
# guaranteed to have an even length. Fall back to 0.0 / skip a trailing
# unpaired entry instead of crashing (a plain dict lookup here would
# raise KeyError/IndexError on real sequences).
energy = 0.0
for i in range(0, len(dimers) - 1, 2):
context = stacking.get(dimers[i])
if context is None:
continue
energy += context.get(dimers[i + 1][::-1], 0.0)
return energy


def compute_final_features(
Expand All @@ -470,7 +481,7 @@ def compute_final_features(
sequence, structure = vals[0], vals[1]
p_idx = adjust_partner_index(return_partner_index(structure))
_,count= return_connection_length(p_idx)
dimers = return_dimers_array(sequence, p_idx)
dimers, _anti_seq = return_dimers_array(sequence, p_idx) # BUG FIX: was assigned the raw (dimers, anti_seq) tuple
gc = _count_gc_content(sequence, p_idx) / count if count else 0.0
ids_gc[seq_id] = [gc, _calculate_free_energy(dimers)]
ids_mono_di[seq_id] = [_count_monomer(sequence, p_idx), _count_dimer(dimers)]
Expand Down
Loading
Loading