Skip to content

Commit

Permalink
Merge pull request #46 from AlexandrovLab/docker_cli
Browse files Browse the repository at this point in the history
Add CLI and container updates
  • Loading branch information
mdbarnesUCSD authored Feb 23, 2024
2 parents 1feb78a + 76c1408 commit 41c7247
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 6 deletions.
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Start with a base Ubuntu image and install Python
FROM ubuntu:22.04

# Avoid prompts from apt
ARG DEBIAN_FRONTEND=noninteractive

# Install Python and other dependencies, and apply updates
RUN apt-get update && apt-get upgrade -y && \
apt-get install -y python3-pip python3-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/*


# Set the working directory in the container
WORKDIR /usr/src/app

# Install SigProfilerMatrixGenerator using pip
RUN pip3 install SigProfilerPlotting==1.3.21

# Create a non-root user named 'spm_user'
RUN useradd -m -s /bin/bash spm_user

# Change the ownership of the /usr/src/app directory and its contents to the new non-root user
RUN chown -R spm_user:spm_user /usr/src/app

# Switch to the non-root user for subsequent commands and when running the container
USER spm_user
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
include sigProfilerPlotting/fonts/*
include sigProfilerPlotting/reference_formats/*
include sigProfilerPlotting/controllers/*
include tests/*
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,23 @@ This section will guide you through the minimum steps required to plot mutationa
pip install SigProfilerPlotting
```

2. From within a python session, you can now plot your mutational matrices as follows:
2. Plot mutational matrices from a Python session or using the Command Line Interface (CLI) as follows:

Using the Python session, the command is as follows:
```python
$ python3
>> import sigProfilerPlotting as sigPlt
>> sigPlt.plotSBS(matrix_path, output_path, project, plot_type, percentage=False)
```
The available functions are listed below and the layout of the required parameters are as follows:

sigPlt.plotSBS(matrix_path, output_path, project, plot_type)
The required parameters are:

sigPlt.plotSBS(matrix_path, output_path, project, plot_type)

where matrix_path, output_path, project, and plot_type must be strings (surrounded by quotation marks, ex: "/Users/ebergstr/Desktop/test_sample").
Using the CLI, the command is as follows:
```bash
SigProfilerPlotting plotSBS <matrix_path> <output_path> <project> <plot_type>
```

3. The final plots are saved into the user-provided output folder.

Expand Down
10 changes: 8 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def readme():
return f.read()


VERSION = "1.3.20"
VERSION = "1.3.21"


def write_version_py(filename="sigProfilerPlotting/version.py"):
Expand All @@ -23,7 +23,7 @@ def write_version_py(filename="sigProfilerPlotting/version.py"):
# THIS FILE IS GENERATED FROM SIGPROFILERPLOTTING SETUP.PY
short_version = '%(version)s'
version = '%(version)s'
update = 'Upgrade v1.3.20: Add np.ndarray to process_input'
update = 'Upgrade v1.3.21: Add CLI and container updates'
"""
fh = open(filename, "w")
Expand Down Expand Up @@ -56,6 +56,7 @@ def write_version_py(filename="sigProfilerPlotting/version.py"):
"sigProfilerPlotting",
"sigProfilerPlotting.reference_formats",
"sigProfilerPlotting.fonts",
"sigProfilerPlotting.controllers",
],
install_requires=[
"matplotlib>=3.4.3",
Expand All @@ -70,6 +71,11 @@ def write_version_py(filename="sigProfilerPlotting/version.py"):
"numpy>=1.21.2",
],
},
entry_points={
"console_scripts": [
"SigProfilerPlotting=sigProfilerPlotting.sigProfilerPlotting_CLI:main_function",
],
},
package_data={"": ["fonts/*.ttf"]},
include_package_data=True,
zip_safe=False,
Expand Down
1 change: 1 addition & 0 deletions sigProfilerPlotting/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import sigProfilerPlotting
231 changes: 231 additions & 0 deletions sigProfilerPlotting/controllers/cli_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import argparse
from typing import List
import sigProfilerPlotting as sigPlt


# Common parser setup for shared arguments
def common_plotting_arguments(parser):
parser.add_argument("matrix_path", help="The path to the input matrix file.")
parser.add_argument(
"output_path", help="The directory where the plots will be saved."
)
parser.add_argument("project", help="The name of the project.")
parser.add_argument("plot_type", help="The type of plot to generate.")
parser.add_argument(
"--percentage", action="store_true", help="Display percentages in the plot."
)
parser.add_argument(
"--custom_text_upper", help="Custom text to display at the top of the plot."
)
parser.add_argument(
"--custom_text_middle", help="Custom text to display in the middle of the plot."
)
parser.add_argument(
"--custom_text_bottom", help="Custom text to display at the bottom of the plot."
)
parser.add_argument(
"--savefig_format",
default="pdf",
choices=["pdf", "png", "pil_image"],
help="The file format for saving the plot.",
)
parser.add_argument("--volume", help="Specify a volume for Docker container usage.")
parser.add_argument(
"--dpi",
type=int,
default=100,
help="The resolution of the plot in dots per inch.",
)


def parse_arguments_sbs(args: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="SigProfilerPlotting plotDBS", description="Generate DBS plots."
)
common_plotting_arguments(parser)
return parser.parse_args(args)


def parse_arguments_id(args: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="SigProfilerPlotting plotID", description="Generate ID plots."
)
common_plotting_arguments(parser)
return parser.parse_args(args)


def parse_arguments_dbs(args: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="SigProfilerPlotting plotDBS", description="Generate DBS plots."
)
common_plotting_arguments(parser)
return parser.parse_args(args)


def parse_arguments_sv(args: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="SigProfilerPlotting plotSV", description="Generate SV plots."
)
parser.add_argument("matrix_path", help="The path to the input matrix file.")
parser.add_argument(
"output_path", help="The directory where the plots will be saved."
)
parser.add_argument("project", help="The name of the project.")
parser.add_argument(
"--percentage", action="store_true", help="Display percentages in the plot."
)
parser.add_argument(
"--aggregate", action="store_true", help="Aggregate all samples."
)
parser.add_argument(
"--savefig_format",
default="pdf",
choices=["pdf", "png", "pil_image"],
help="The file format for saving the plot.",
)
parser.add_argument(
"--dpi",
type=int,
default=100,
help="The resolution of the plot in dots per inch.",
)
return parser.parse_args(args)


def parse_arguments_cnv(args: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="SigProfilerPlotting plotCNV", description="Generate CNV plots."
)
parser.add_argument("matrix_path", help="The path to the input matrix file.")
parser.add_argument(
"output_path", help="The directory where the plots will be saved."
)
parser.add_argument("project", help="The name of the project.")
parser.add_argument(
"--percentage", action="store_true", help="Display percentages in the plot."
)
parser.add_argument(
"--aggregate", action="store_true", help="Aggregate data for the plot."
)
parser.add_argument(
"--read_from_file",
action="store_true",
default=True,
help="Read data from a file for the plot.",
)
parser.add_argument(
"--savefig_format",
default="pdf",
choices=["pdf", "png", "pil_image"],
help="The file format for saving the plot.",
)
parser.add_argument(
"--dpi",
type=int,
default=100,
help="The resolution of the plot in dots per inch.",
)
return parser.parse_args(args)


def dispatch_plot_sbs(parsed_args: argparse.Namespace) -> None:
sigPlt.plotSBS(
matrix_path=parsed_args.matrix_path,
output_path=parsed_args.output_path,
project=parsed_args.project,
plot_type=parsed_args.plot_type,
percentage=parsed_args.percentage,
custom_text_upper=parsed_args.custom_text_upper,
custom_text_middle=parsed_args.custom_text_middle,
custom_text_bottom=parsed_args.custom_text_bottom,
savefig_format=parsed_args.savefig_format,
volume=parsed_args.volume,
dpi=parsed_args.dpi,
)


def dispatch_plot_id(parsed_args: argparse.Namespace) -> None:
sigPlt.plotID(
matrix_path=parsed_args.matrix_path,
output_path=parsed_args.output_path,
project=parsed_args.project,
plot_type=parsed_args.plot_type,
percentage=parsed_args.percentage,
custom_text_upper=parsed_args.custom_text_upper,
custom_text_middle=parsed_args.custom_text_middle,
custom_text_bottom=parsed_args.custom_text_bottom,
savefig_format=parsed_args.savefig_format,
volume=parsed_args.volume,
dpi=parsed_args.dpi,
)


def dispatch_plot_dbs(parsed_args: argparse.Namespace) -> None:
sigPlt.plotDBS(
matrix_path=parsed_args.matrix_path,
output_path=parsed_args.output_path,
project=parsed_args.project,
plot_type=parsed_args.plot_type,
percentage=parsed_args.percentage,
custom_text_upper=parsed_args.custom_text_upper,
custom_text_middle=parsed_args.custom_text_middle,
custom_text_bottom=parsed_args.custom_text_bottom,
savefig_format=parsed_args.savefig_format,
volume=parsed_args.volume,
dpi=parsed_args.dpi,
)


def dispatch_plot_sv(parsed_args: argparse.Namespace) -> None:
sigPlt.plotSV(
matrix_path=parsed_args.matrix_path,
output_path=parsed_args.output_path,
project=parsed_args.project,
percentage=parsed_args.percentage,
aggregate=parsed_args.aggregate,
savefig_format=parsed_args.savefig_format,
dpi=parsed_args.dpi,
)


def dispatch_plot_cnv(parsed_args: argparse.Namespace) -> None:
sigPlt.plotCNV(
matrix_path=parsed_args.matrix_path,
output_path=parsed_args.output_path,
project=parsed_args.project,
percentage=parsed_args.percentage,
aggregate=parsed_args.aggregate,
read_from_file=parsed_args.read_from_file,
savefig_format=parsed_args.savefig_format,
dpi=parsed_args.dpi,
)


class CliController:
def dispatch(self, user_args: List[str]):
if "plotSBS" in user_args:
parsed_args = parse_arguments_sbs(user_args[1:])
dispatch_plot_sbs(parsed_args)
elif "plotID" in user_args:
parsed_args = parse_arguments_id(user_args[1:])
dispatch_plot_id(parsed_args)
elif "plotDBS" in user_args:
parsed_args = parse_arguments_dbs(user_args[1:])
dispatch_plot_dbs(parsed_args)
elif "plotSV" in user_args:
parsed_args = parse_arguments_sv(user_args[1:])
dispatch_plot_sv(parsed_args)
elif "plotCNV" in user_args:
parsed_args = parse_arguments_cnv(user_args[1:])
dispatch_plot_cnv(parsed_args)
else:
print(
"Unknown command. Available commands: plotSBS, plotID, plotDBS, plotSV, plotCNV."
)


if __name__ == "__main__":
import sys

controller = CliController()
controller.dispatch(sys.argv)
46 changes: 46 additions & 0 deletions sigProfilerPlotting/sigProfilerPlotting_CLI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3

import sys
from sigProfilerPlotting import sigProfilerPlotting
from sigProfilerPlotting.controllers import cli_controller


def main_function():
commands = {
"plotSBS": "Plot Single Base Substitutions.",
"plotID": "Plot Small Insertions and Deletions.",
"plotDBS": "Plot Doublet Base Substitutions.",
"plotSV": "Plot Structural Variations.",
"plotCNV": "Plot Copy Number Variations.",
}

if len(sys.argv) < 2 or sys.argv[1] not in commands.keys():
print_usage(commands)
sys.exit(1)

command = sys.argv[1]
args = sys.argv[1:]

controller = cli_controller.CliController()
valid_commands = {"plotSBS", "plotID", "plotDBS", "plotSV", "plotCNV"}

if command in valid_commands:
controller.dispatch(args)
else:
print_usage(commands)
sys.exit(1)


def print_usage(commands):
"""Prints the usage message."""
print("Usage: SigProfilerPlotting <command> [<args>]\n")
print("Commands:")
for cmd, desc in commands.items():
print(f" {cmd}: {desc}")
print(
"\nUse 'SigProfilerPlotting <command> --help' for more information on a specific command."
)


if __name__ == "__main__":
main_function()

0 comments on commit 41c7247

Please sign in to comment.