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

Improve command output with text banner, colour and alignment #113

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3bcabef
Define ASCII art text banner w/ and w/o colour for CATS
sadielbartholomew Aug 22, 2024
67d2284
Escape some chars to avoid SyntaxWarning from ASCII art text banner
sadielbartholomew Aug 26, 2024
d8f12e0
Add new --no-colour/-n CLI option to toggle terminal colours
sadielbartholomew Aug 26, 2024
7de3050
Command output: align 'best job start time' w/ other output
sadielbartholomew Aug 26, 2024
6c56332
Command output: colour output to distinguish now vs. optimal
sadielbartholomew Aug 26, 2024
17e02d2
Subsume output informational message to logging system
sadielbartholomew Aug 26, 2024
28e44ae
Final escaping & adjust colours to traffic light colour rating
sadielbartholomew Aug 26, 2024
0803922
Move banner printout so it comes before any log- or printing
sadielbartholomew Aug 26, 2024
4769de7
Make clearer CLI description for --no-colour option
sadielbartholomew Aug 26, 2024
884c2dd
Command output: change CATS letting from white to default colour
sadielbartholomew Sep 30, 2024
637c9df
Fix some typos in comments & docstrings
sadielbartholomew Sep 30, 2024
56fb80e
CLI: add --no-color as alias to --no-colour
sadielbartholomew Sep 30, 2024
6499544
Fix further typos, in CLI interface (user-facing)
sadielbartholomew Sep 30, 2024
5eb5ac4
Fix bug whereby colour parameter was not assigned a default
sadielbartholomew Sep 30, 2024
e648100
Update tests to account for new colour arg for passing test suite
sadielbartholomew Sep 30, 2024
fc105a5
Fix for CATSOutput new better order of args
sadielbartholomew Oct 1, 2024
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
67 changes: 58 additions & 9 deletions cats/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .configure import get_runtime_config
from .forecast import CarbonIntensityAverageEstimate
from .optimise_starttime import get_avg_estimates # noqa: F401
from .constants import CATS_ASCII_BANNER_COLOUR, CATS_ASCII_BANNER_NO_COLOUR

__version__ = "1.0.0"

Expand Down Expand Up @@ -170,18 +171,29 @@
parser.add_argument(
"--cpu",
type=positive_integer,
help="Number of cpus used by the job",
help="Number of CPUs used by the job",
)
parser.add_argument(
"--gpu",
type=positive_integer,
help="Number of cpus used by the job",
help="Number of GPUs used by the job",
)
parser.add_argument(
"--memory",
type=positive_integer,
help="Amount of memory used by the job, in GB",
)
parser.add_argument(
"-n",
"--no-colour",
action="store_true",
help="Disable all terminal output colouring",
)
parser.add_argument(
"--no-color",
action="store_true",
help="Disable all terminal output colouring (alias to --no-colour)",
)

return parser

Expand All @@ -195,18 +207,40 @@
location: str
countryISO3: str
emmissionEstimate: Optional[Estimates] = None
colour: bool = False

def __str__(self) -> str:
out = f"""Best job start time {self.carbonIntensityOptimal.start}
Carbon intensity if job started now = {self.carbonIntensityNow.value:.2f} gCO2eq/kWh
Carbon intensity at optimal time = {self.carbonIntensityOptimal.value:.2f} gCO2eq/kWh"""
if self.colour:
# Default colour

Check warning on line 214 in cats/__init__.py

View check run for this annotation

Codecov / codecov/patch

cats/__init__.py#L214

Added line #L214 was not covered by tests
col_normal = "\33[0m" # reset any colour

# Colours to indicate optimal/better results
col_dt_opt = "\33[32m" # green i.e. 'good' in traffic light rating
col_ci_opt = "\33[32m" # green

Check warning on line 219 in cats/__init__.py

View check run for this annotation

Codecov / codecov/patch

cats/__init__.py#L217-L219

Added lines #L217 - L219 were not covered by tests
col_ee_opt = "\33[32m" # green

# Colours to indicate original and non-optimal results
col_ci_now = "\33[31m" # red i.e. 'bad' in traffic light rating

Check warning on line 223 in cats/__init__.py

View check run for this annotation

Codecov / codecov/patch

cats/__init__.py#L222-L223

Added lines #L222 - L223 were not covered by tests
col_ee_now = "\33[31m" # red
else:
col_normal = ""
col_dt_opt = ""
col_ci_opt = ""
col_ci_now = ""
col_ee_now = ""
col_ee_opt = ""

out = f"""
Best job start time = {col_dt_opt}{self.carbonIntensityOptimal.start}{col_normal}
Carbon intensity if job started now = {col_ci_now}{self.carbonIntensityNow.value:.2f} gCO2eq/kWh{col_normal}
Carbon intensity at optimal time = {col_ci_opt}{self.carbonIntensityOptimal.value:.2f} gCO2eq/kWh{col_normal}"""

if self.emmissionEstimate:
out += f"""
Estimated emissions if job started now = {self.emmissionEstimate.now}
Estimated emissions at optimal time = {self.emmissionEstimate.best} (- {self.emmissionEstimate.savings})"""
Estimated emissions if job started now = {col_ee_now}{self.emmissionEstimate.now}{col_normal}
Estimated emissions at optimal time = {col_ee_opt}{self.emmissionEstimate.best} (- {self.emmissionEstimate.savings}){col_normal}"""

out += "\n\nUse --format=json to get this in machine readable format"
logging.info("Use '--format=json' to get this in machine readable format")
return out

def to_json(self, dateformat: str = "", **kwargs) -> str:
Expand All @@ -222,6 +256,15 @@
return json.dumps(data, **kwargs)


def print_banner(disable_colour):
"""Print an ASCII art banner with the CATS title, optionally in colour.
"""
if disable_colour:

Check warning on line 262 in cats/__init__.py

View check run for this annotation

Codecov / codecov/patch

cats/__init__.py#L262

Added line #L262 was not covered by tests
print(CATS_ASCII_BANNER_NO_COLOUR)
else:
print(CATS_ASCII_BANNER_COLOUR)


def schedule_at(
output: CATSOutput, args: list[str], at_command: str = "at"
) -> Optional[str]:
Expand Down Expand Up @@ -251,6 +294,11 @@
def main(arguments=None) -> int:
parser = parse_arguments()
args = parser.parse_args(arguments)
colour_output = args.no_colour or args.no_color

# Print CATS ASCII art banner, before any output from printing or logging
print_banner(colour_output)

if args.command and not args.scheduler:
print(
"cats: To run a command with the -c or --command option, you must\n"
Expand Down Expand Up @@ -287,7 +335,8 @@
# Find best possible average carbon intensity, along
# with corresponding job start time.
now_avg, best_avg = get_avg_estimates(CI_forecast, duration=duration)
output = CATSOutput(now_avg, best_avg, location, "GBR")
output = CATSOutput(
now_avg, best_avg, location, "GBR", colour=not colour_output)

################################
## Calculate carbon footprint ##
Expand Down
4 changes: 2 additions & 2 deletions cats/configure.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""This module exports a function :py:func:`configure
<cats.configure.configure>` that processes both command line arguments
and configuration file. This function returns a runtime configuration
for cats to make a request to a carcon intensity forecast provider. A
for cats to make a request to a carbon intensity forecast provider. A
runtime configuration consits of:

- location (postcode)
- job duration
- Interface to carbon intensity forecast provider (See TODO)
- interface to carbon intensity forecast provider (See TODO)

"""

Expand Down
19 changes: 19 additions & 0 deletions cats/constants.py
Original file line number Diff line number Diff line change
@@ -1 +1,20 @@
MEMORY_POWER_PER_GB = 0.3725 # W/GB

CATS_ASCII_BANNER_COLOUR = """
\33[34mThe\33[35m.\33[34m\33[0m____ \33[35m..... \33[0m__ \33[35m.... \33[0m________ \33[35m. \33[0m______\33[35m...
\33[35m.. \33[0m/ __)\33[35m.....\33[0m/ \\\33[35m....\33[0m(__ __)\33[35m.\33[0m) ____)\33[35m....
\33[35m..\33[0m| /\33[35m.......\33[0m/ \\\33[35m......\33[0m| |\33[35m...\33[0m( (___\33[35m........
\33[35m..\33[0m| |\33[34mlimate\33[35m.\33[0m/ () \\\33[34mware\33[35m.\33[0m| |\33[34mask\33[35m.\33[0m\\___ \\\33[34mcheduler
\33[35m..\33[0m| \\__\33[35m...\33[0m| __ |\33[35m....\33[0m| |\33[35m....\33[0m____) )\33[35m....
\33[35m...\33[0m\\ )\33[35m..\33[0m| (\33[35m..\33[0m) |\33[35m....\33[0m| |\33[35m...\33[0m( (\33[35m..\33[0m
"""

# Same as above but without the colour codes, so not given custom colouring
CATS_ASCII_BANNER_NO_COLOUR = """
The.____ ..... __ .... ________ . ______ ...
.. / __)...../ \\....(__ __).) ____)....
..| /......./ \\......| |...( (___........
..| |limate./ () \\ware.| |ask.\\___ \\cheduler
..| \\__...| __ |....| |....____) )....
...\\ )..| (..) |....| |...( (..
"""
22 changes: 10 additions & 12 deletions tests/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,19 @@
[
(
OUTPUT,
"""Best job start time 2024-03-16 02:00:00
"""
Best job start time = 2024-03-16 02:00:00
Carbon intensity if job started now = 50.00 gCO2eq/kWh
Carbon intensity at optimal time = 20.00 gCO2eq/kWh

Use --format=json to get this in machine readable format""",
Carbon intensity at optimal time = 20.00 gCO2eq/kWh""",
),
(
OUTPUT_WITH_EMISSION_ESTIMATE,
"""Best job start time 2024-03-16 02:00:00
"""
Best job start time = 2024-03-16 02:00:00
Carbon intensity if job started now = 50.00 gCO2eq/kWh
Carbon intensity at optimal time = 20.00 gCO2eq/kWh
Estimated emissions if job started now = 19
Estimated emissions at optimal time = 9 (- 10)

Use --format=json to get this in machine readable format""",
Estimated emissions at optimal time = 9 (- 10)""",
),
],
)
Expand All @@ -62,13 +60,13 @@ def test_string_repr(output, expected):
OUTPUT,
"""{"carbonIntensityNow": {"end": "2024-03-15T17:00:00", "start": "2024-03-15T16:00:00", "value": 50}, """
""""carbonIntensityOptimal": {"end": "2024-03-16T03:00:00", "start": "2024-03-16T02:00:00", "value": 20}, """
""""countryISO3": "GBR", "emmissionEstimate": null, "location": "OX1"}""",
""""colour": false, "countryISO3": "GBR", "emmissionEstimate": null, "location": "OX1"}""",
),
(
OUTPUT_WITH_EMISSION_ESTIMATE,
"""{"carbonIntensityNow": {"end": "2024-03-15T17:00:00", "start": "2024-03-15T16:00:00", "value": 50}, """
""""carbonIntensityOptimal": {"end": "2024-03-16T03:00:00", "start": "2024-03-16T02:00:00", "value": 20}, """
""""countryISO3": "GBR", "emmissionEstimate": [19, 9, 10], "location": "OX1"}""",
""""colour": false, "countryISO3": "GBR", "emmissionEstimate": [19, 9, 10], "location": "OX1"}""",
),
],
)
Expand All @@ -83,13 +81,13 @@ def test_output_json(output, expected):
OUTPUT,
"""{"carbonIntensityNow": {"end": "202403151700", "start": "202403151600", "value": 50}, """
""""carbonIntensityOptimal": {"end": "202403160300", "start": "202403160200", "value": 20}, """
""""countryISO3": "GBR", "emmissionEstimate": null, "location": "OX1"}""",
""""colour": false, "countryISO3": "GBR", "emmissionEstimate": null, "location": "OX1"}""",
),
(
OUTPUT_WITH_EMISSION_ESTIMATE,
"""{"carbonIntensityNow": {"end": "202403151700", "start": "202403151600", "value": 50}, """
""""carbonIntensityOptimal": {"end": "202403160300", "start": "202403160200", "value": 20}, """
""""countryISO3": "GBR", "emmissionEstimate": [19, 9, 10], "location": "OX1"}""",
""""colour": false, "countryISO3": "GBR", "emmissionEstimate": [19, 9, 10], "location": "OX1"}""",
),
],
)
Expand Down