Skip to content

Commit 8bf4694

Browse files
authored
Merge pull request #131 from AlexandrovLab/development
Development
2 parents 7077caf + a56f325 commit 8bf4694

17 files changed

+176
-28
lines changed

.travis.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ before_install:
1616
fi
1717

1818
install:
19-
- pip install .
19+
- pip install .[tests]
2020

2121
before_script:
2222
- python3 install_genome.py $TRAVIS_BUILD_DIR/src/
2323

24-
script: python3 test.py
24+
script:
25+
# run unit tests
26+
- pytest tests
27+
# run integration tests
28+
- python3 test.py

CHANGELOG.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
# Changelog
3+
4+
All notable changes to this project will be documented in this file.
5+
6+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [0.1.7] - 2024-06-21
11+
12+
### Added
13+
- Added CLI boolean handling to improve command-line interface usability.
14+
- Added new pytests for CLI to ensure correct handling of booleans.
15+
- Updated the README to reflect the new pytest additions.
16+
- Updated CI/CD pipelines to accommodate pytest changes.
17+
18+
### Changed
19+
- Improved input_type value check mechanism to use .lower() before checking against values 'vcf' or 'matrix'.
20+
- Updated dependency from PyPDF2 to pypdf to increase compatibility and resolve installation issues on bioconda.
21+
22+

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ Analyze.cosmic_fit(samples=spa.__path__[0]+"/data/tests/txt_input/sample_matrix_
160160
## <a name="denovo"></a> _De novo_ extraction of mutational signatures downstream analysis
161161
Additional functionalities for downstream analysis of _de novo_ extraction of mutational signatures are also available as part of SigProfilerAssignment, including assignment of _de novo_ extracted mutational signatures and decomposition of _de novo_ signatures using a known set of signatures. More information can be found on the wiki page at https://osf.io/mz79v/wiki/5.%20Advanced%20mode/.
162162

163+
## <a name="unit_tests"></a> Unit Tests
164+
Unit tests can be run with the following commands:
165+
166+
```bash
167+
python setup.py sdist
168+
pip install .[tests]
169+
pytest tests
170+
```
171+
163172
## <a name="citation"></a> Citation
164173

165174
Díaz-Gay, M., Vangara, R., Barnes, M., ... & Alexandrov, L. B. (2023). Assigning mutational signatures to individual samples and individual somatic mutations with SigProfilerAssignment, Bioinformatics, 2023-07. doi: [https://doi.org/10.1093/bioinformatics/btad756](https://doi.org/10.1093/bioinformatics/btad756)

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_CNV48.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_DBS78.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_ID83.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_SBS1536.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_SBS288.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_SBS96.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/DecompositionPlots/PlotDecomposition_SV32.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from reportlab.pdfbase.ttfonts import TTFont
1212
from reportlab.pdfbase import pdfmetrics
1313
import SigProfilerAssignment as spa_path
14-
from PyPDF2 import PdfWriter, PdfReader
14+
from pypdf import PdfWriter, PdfReader
1515

1616
# imports for saving plots to memory
1717
import io

SigProfilerAssignment/controllers/cli_controller.py

+38-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
from SigProfilerAssignment import decomposition as decomp
44

55

6+
def str2bool(v):
7+
if isinstance(v, bool):
8+
return v
9+
if v.lower() in ("yes", "true", "t", "y", "1"):
10+
return True
11+
elif v.lower() in ("no", "false", "f", "n", "0"):
12+
return False
13+
else:
14+
raise argparse.ArgumentTypeError("Boolean value expected.")
15+
16+
617
def parse_arguments_common(args: List[str], description: str) -> argparse.Namespace:
718
parser = argparse.ArgumentParser(description=description)
819

@@ -43,22 +54,36 @@ def parse_arguments_common(args: List[str], description: str) -> argparse.Namesp
4354
help="COSMIC version (default: 3.4). Valid options: {1, 2, 3, 3.1, 3.2, 3.3, 3.4}.",
4455
)
4556
parser.add_argument(
46-
"--make_plots", type=bool, default=True, help="Generate plots (default: True)."
57+
"--make_plots",
58+
type=str2bool,
59+
nargs="?",
60+
const=True,
61+
default=True,
62+
help="Generate plots (default: True).",
4763
)
4864
parser.add_argument(
4965
"--collapse_to_SBS96",
50-
type=bool,
66+
type=str2bool,
67+
nargs="?",
68+
const=True,
5169
default=True,
5270
help="Collapse to SBS96 (default: True).",
5371
)
5472
parser.add_argument(
5573
"--connected_sigs",
56-
type=bool,
74+
type=str2bool,
75+
nargs="?",
76+
const=True,
5777
default=True,
5878
help="Connected signatures (default: True).",
5979
)
6080
parser.add_argument(
61-
"--verbose", type=bool, default=False, help="Verbose output (default: False)."
81+
"--verbose",
82+
type=str2bool,
83+
nargs="?",
84+
const=True,
85+
default=False,
86+
help="Verbose output (default: False).",
6287
)
6388
parser.add_argument(
6489
"--new_signature_thresh_hold",
@@ -73,7 +98,9 @@ def parse_arguments_common(args: List[str], description: str) -> argparse.Namesp
7398
)
7499
parser.add_argument(
75100
"--exome",
76-
type=bool,
101+
type=str2bool,
102+
nargs="?",
103+
const=True,
77104
default=False,
78105
help="Use exome renormalized COSMIC signatures (default: False).",
79106
)
@@ -89,13 +116,17 @@ def parse_arguments_common(args: List[str], description: str) -> argparse.Namesp
89116
)
90117
parser.add_argument(
91118
"--export_probabilities",
92-
type=bool,
119+
type=str2bool,
120+
nargs="?",
121+
const=True,
93122
default=True,
94123
help="Export probabilities matrix per mutational context (default: True).",
95124
)
96125
parser.add_argument(
97126
"--export_probabilities_per_mutation",
98-
type=bool,
127+
type=str2bool,
128+
nargs="?",
129+
const=True,
99130
default=False,
100131
help="Export probabilities matrices per mutation (default: False).",
101132
)

SigProfilerAssignment/decompose_subroutines.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727
from sigProfilerPlotting import plotActivity as plot_ac
2828
from sigProfilerPlotting import tmbplot as tmb
2929
import string
30-
import PyPDF2
30+
import pypdf
3131
import scipy
3232

3333
# import SigProfilerAssignment as sspro
34-
from PyPDF2 import PdfMerger
34+
from pypdf import PdfMerger
3535
import SigProfilerAssignment as spa
3636
from SigProfilerAssignment import single_sample as ss
3737
from scipy.spatial.distance import correlation as cor
@@ -1776,10 +1776,10 @@ def merge_pdf(input_folder, output_file):
17761776
pdf2merge.append(filename)
17771777

17781778
pdf2merge.sort()
1779-
pdfWriter = PyPDF2.PdfFileWriter()
1779+
pdfWriter = pypdf.PdfFileWriter()
17801780
for filename in pdf2merge:
17811781
pdfFileObj = open(input_folder + "/" + filename, "rb")
1782-
pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
1782+
pdfReader = pypdf.PdfFileReader(pdfFileObj)
17831783
for pageNum in range(pdfReader.numPages):
17841784
pageObj = pdfReader.getPage(pageNum)
17851785
pdfWriter.addPage(pageObj)

SigProfilerAssignment/decomposition.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from sigProfilerPlotting import sigProfilerPlotting as sigPlot
2828
import sigProfilerPlotting
2929
import os, sys
30-
from PyPDF2 import PdfMerger
30+
from pypdf import PdfMerger
3131
import fitz
3232
import time
3333
from pathlib import Path
@@ -305,7 +305,7 @@ def spa_analyze(
305305
"If denovo_refit or decompose_fit is True, signatures cannot be empty"
306306
)
307307

308-
if input_type == "vcf":
308+
if input_type.lower() == "vcf":
309309
project_name = "Input_vcffiles"
310310
vcf_context = context_type
311311
data = datadump.SigProfilerMatrixGeneratorFunc(
@@ -329,7 +329,7 @@ def spa_analyze(
329329
)
330330
genomes = genomes.set_index("MutationType")
331331

332-
elif input_type == "matrix":
332+
elif input_type.lower() == "matrix":
333333
try:
334334
genomes = pd.read_csv(samples, sep="\t", index_col=0)
335335
except:

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ statsmodels
88
scikit-learn
99
psutil
1010
reportlab
11-
PyPDF2
11+
pypdf
1212
alive_progress

setup.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
if os.path.exists("dist"):
77
shutil.rmtree("dist")
88

9-
VERSION = "0.1.6"
9+
VERSION = "0.1.7"
1010

1111

1212
def write_version_py(filename="SigProfilerAssignment/version.py"):
@@ -15,7 +15,7 @@ def write_version_py(filename="SigProfilerAssignment/version.py"):
1515
# THIS FILE IS GENERATED FROM SigProfilerAssignment SETUP.PY
1616
short_version = '%(version)s'
1717
version = '%(version)s'
18-
Update = 'v0.1.6: Add CLI and volume parameter for template files.'
18+
Update = 'v0.1.7: Update CLI, add pytest, and update pypdf dependency.'
1919
2020
2121
"""
@@ -42,7 +42,7 @@ def write_version_py(filename="SigProfilerAssignment/version.py"):
4242
"scikit-learn>=0.24.2",
4343
"psutil>=5.6.1",
4444
"reportlab>=3.5.42",
45-
"PyPDF2>=3.0.0",
45+
"pypdf>=3.1.0",
4646
"alive_progress>=2.4.1",
4747
"pdf2image>=1.16.0",
4848
"PyMuPDF>=1.21.0",
@@ -61,6 +61,11 @@ def write_version_py(filename="SigProfilerAssignment/version.py"):
6161
license="UCSD",
6262
packages=["SigProfilerAssignment"],
6363
install_requires=requirements,
64+
extras_require={
65+
"tests": [
66+
"pytest",
67+
],
68+
},
6469
include_package_data=True,
6570
entry_points={
6671
"console_scripts": [

test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import SigProfilerAssignment as spa
99
from SigProfilerAssignment import Analyzer as Analyze
1010
from SigProfilerAssignment.DecompositionPlots import PlotDecomposition as sp
11-
from PyPDF2 import PdfMerger
11+
from pypdf import PdfMerger
1212
import numpy as np
1313
import pandas as pd
1414
import time

tests/test_cli.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
import argparse
3+
from SigProfilerAssignment.controllers.cli_controller import (
4+
parse_arguments_common,
5+
str2bool,
6+
)
7+
8+
9+
def test_default_values():
10+
args = parse_arguments_common(
11+
["dummy_sample", "dummy_output"], "Test default values"
12+
)
13+
assert args.make_plots == True
14+
assert args.collapse_to_SBS96 == True
15+
assert args.connected_sigs == True
16+
assert args.verbose == False
17+
assert args.export_probabilities == True
18+
assert args.export_probabilities_per_mutation == False
19+
assert args.exome == False
20+
21+
22+
def test_argument_parsing():
23+
args = parse_arguments_common(
24+
[
25+
"dummy_sample",
26+
"dummy_output",
27+
"--genome_build",
28+
"GRCh38",
29+
"--cosmic_version",
30+
"3.4",
31+
"--make_plots",
32+
"False",
33+
"--collapse_to_SBS96",
34+
"False",
35+
"--connected_sigs",
36+
"False",
37+
"--verbose",
38+
"True",
39+
"--export_probabilities",
40+
"False",
41+
"--export_probabilities_per_mutation",
42+
"True",
43+
"--exome",
44+
"True",
45+
],
46+
"Test argument parsing",
47+
)
48+
assert args.samples == "dummy_sample"
49+
assert args.output == "dummy_output"
50+
assert args.genome_build == "GRCh38"
51+
assert args.cosmic_version == 3.4
52+
assert args.make_plots == False
53+
assert args.collapse_to_SBS96 == False
54+
assert args.connected_sigs == False
55+
assert args.verbose == True
56+
assert args.export_probabilities == False
57+
assert args.export_probabilities_per_mutation == True
58+
assert args.exome == True
59+
60+
61+
def test_boolean_conversion():
62+
assert str2bool("yes") == True
63+
assert str2bool("true") == True
64+
assert str2bool("t") == True
65+
assert str2bool("y") == True
66+
assert str2bool("1") == True
67+
assert str2bool("no") == False
68+
assert str2bool("false") == False
69+
assert str2bool("f") == False
70+
assert str2bool("n") == False
71+
assert str2bool("0") == False
72+
with pytest.raises(argparse.ArgumentTypeError):
73+
str2bool("maybe")
74+
75+
76+
if __name__ == "__main__":
77+
pytest.main()

0 commit comments

Comments
 (0)