Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various enhancements for plot command #217

Merged
merged 6 commits into from
Nov 6, 2024
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
__pycache__/
src/__pycache__/
con_duct.egg-info/
*.egg-info/
.coverage
coverage.*
.duct/
.idea
.tox/
build/
dist/
venvs/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ These helpers may use 3rd party python libraries.

usage: con-duct <command> [options]

A command-line tool for managing various tasks.
A suite of commands to manage or manipulate con-duct logs.

positional arguments:
{pp,plot} Available subcommands
pp Pretty print a JSON log
pp Pretty print a JSON log.
plot Plot resource usage for an execution.

options:
Expand Down
10 changes: 5 additions & 5 deletions src/con_duct/suite/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@ def execute(args: argparse.Namespace) -> int:
def main(argv: Optional[List[str]] = None) -> None:
parser = argparse.ArgumentParser(
prog="con-duct",
description="A command-line tool for managing various tasks.",
description="A suite of commands to manage or manipulate con-duct logs.",
usage="con-duct <command> [options]",
)
subparsers = parser.add_subparsers(dest="command", help="Available subcommands")

# Subcommand: pp
parser_pp = subparsers.add_parser("pp", help="Pretty print a JSON log")
parser_pp.add_argument("file_path", help="JSON file to pretty print")
parser_pp = subparsers.add_parser("pp", help="Pretty print a JSON log.")
parser_pp.add_argument("file_path", help="JSON file to pretty print.")
parser_pp.set_defaults(func=pprint_json)

# Subcommand: plot
parser_plot = subparsers.add_parser(
"plot", help="Plot resource usage for an execution."
)
parser_plot.add_argument("file_path", help="duct-produced usage.json file plot.")
parser_plot.add_argument("file_path", help="duct-produced usage.json file.")
parser_plot.add_argument(
"--output",
help="Output path for the png file. If not passed, show the file but do not save.",
help="Output path for the image file. If not specified, plot will be shown and not saved.",
Copy link
Member

Choose a reason for hiding this comment

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

How does the user know the file will be png?

Copy link
Member Author

Choose a reason for hiding this comment

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

he specifies the extension desired. output.png -- will be .png. output.svg -- will be .svg

Copy link
Member

Choose a reason for hiding this comment

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

Oh nice, matplotlib is smarter than I realized.

default=None,
)
# parser_plot.add_argument(
Expand Down
7 changes: 4 additions & 3 deletions src/con_duct/suite/plot.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import argparse
from datetime import datetime
import json
import matplotlib.pyplot as plt
import numpy as np


def matplotlib_plot(args: argparse.Namespace) -> int:
import matplotlib.pyplot as plt
import numpy as np

data = []
try:
with open(args.file_path, "r") as file:
Expand Down Expand Up @@ -52,7 +53,7 @@ def matplotlib_plot(args: argparse.Namespace) -> int:
if args.output is not None:
plt.savefig(args.output)
print(
f"Successfully rendered input file: {args.file_path} to output {args.output}.png"
f"Successfully rendered input file: {args.file_path} to output {args.output}"
)
else:
plt.show()
Expand Down
21 changes: 9 additions & 12 deletions test/test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,40 +101,37 @@ def test_pprint_invalid_json(

class TestPlotMatplotlib(unittest.TestCase):

@patch("con_duct.suite.plot.plt.savefig")
@patch("matplotlib.pyplot.savefig")
def test_matplotlib_plot_sanity(self, mock_plot_save: MagicMock) -> None:
args = argparse.Namespace(
command="plot",
file_path="test/data/mriqc-example/usage.json",
output="outfile",
output="outfile.png",
func=plot.matplotlib_plot,
)
assert main.execute(args) == 0
mock_plot_save.assert_called_once_with("outfile")
mock_plot_save.assert_called_once_with("outfile.png")

@patch("con_duct.suite.plot.plt.savefig")
@patch("builtins.open", side_effect=FileNotFoundError)
def test_matplotlib_plot_file_not_found(
self, _mock_open: MagicMock, mock_plot_save: MagicMock
) -> None:
@patch("matplotlib.pyplot.savefig")
def test_matplotlib_plot_file_not_found(self, mock_plot_save: MagicMock) -> None:
args = argparse.Namespace(
command="plot",
file_path="test/data/mriqc-example/usage.json",
output="outfile",
file_path="test/data/mriqc-example/usage_not_to_be_found.json",
output="outfile.png",
func=plot.matplotlib_plot,
)
assert main.execute(args) == 1
mock_plot_save.assert_not_called()

@patch("con_duct.suite.plot.plt.savefig")
@patch("matplotlib.pyplot.savefig")
@patch("builtins.open", new_callable=mock_open, read_data='{"invalid": "json"')
def test_matplotlib_plot_invalid_json(
self, _mock_open: MagicMock, mock_plot_save: MagicMock
) -> None:
args = argparse.Namespace(
command="plot",
file_path="test/data/mriqc-example/usage.json",
output="outfile",
output="outfile.png",
func=plot.matplotlib_plot,
)
assert main.execute(args) == 1
Expand Down
Loading