Skip to content

Commit 240ed1f

Browse files
authored
refactor: improve structure and adds custom no logs error (#21)
* refactor: improve structure * fix * fix * fix: dont fail for missing logs
1 parent 4a3d2dd commit 240ed1f

File tree

6 files changed

+209
-190
lines changed

6 files changed

+209
-190
lines changed

action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ runs:
290290
# Get static plot list (from input)
291291
read -a plots_array_input <<< ${{ inputs.plots }}
292292
# Get dynamic plot list (from comment script)
293-
read -a plots_array_dynamic <<< "$(python scripts/draft_comment.py plots)"
293+
read -a plots_array_dynamic <<< "$(python src/draft_comment.py plots)"
294294
295295
plots_array=("${plots_array_input[@]}" "${plots_array_dynamic[@]}")
296296
@@ -315,7 +315,7 @@ runs:
315315
done
316316
317317
# Get benchmark plot list (from benchmark script)
318-
read -a plots_array_benchmark <<< "$(python scripts/plot_benchmarks.py)"
318+
read -a plots_array_benchmark <<< "$(python src/plot_benchmarks.py)"
319319
320320
mkdir -p _validation-images/benchmarks
321321
@@ -356,7 +356,7 @@ runs:
356356
357357
# Create comment
358358
# Note: The script uses many env variables. See the script for more details.
359-
python scripts/draft_comment.py > $GITHUB_WORKSPACE/comment.txt
359+
python src/draft_comment.py > $GITHUB_WORKSPACE/comment.txt
360360
361361
cat $GITHUB_WORKSPACE/comment.txt >> $GITHUB_STEP_SUMMARY
362362
Lines changed: 19 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,17 @@
1-
"""
2-
Draft comment for pypsa-validator GitHub PRs.
1+
"""Text components to generate validator report."""
32

4-
Script can be called via command line or imported as a module.
5-
"""
6-
7-
import argparse
83
import os
94
import re
105
from dataclasses import dataclass
116
from pathlib import Path
127

138
import numpy as np
149
import pandas as pd
10+
1511
from metrics import min_max_normalized_mae, normalized_root_mean_square_error
16-
from numpy.typing import ArrayLike
1712
from utils import get_env_var
1813

1914

20-
def create_numeric_mask(arr: ArrayLike) -> np.ndarray:
21-
"""
22-
Create a mask where True indicates numeric and finite values.
23-
24-
Parameters
25-
----------
26-
arr : array-like
27-
Input array
28-
29-
Returns
30-
-------
31-
np.ndarray: Numeric mask where True indicates numeric and finite sort_values
32-
33-
"""
34-
arr = np.array(arr)
35-
return np.vectorize(lambda x: isinstance(x, (int, float)) and np.isfinite(x))(arr)
36-
37-
3815
@dataclass
3916
class CommentData:
4017
"""Class to store data for comment generation."""
@@ -74,12 +51,15 @@ def errors(self, branch_type: str) -> list:
7451
logs = list(
7552
Path(f"{self.dir_artifacts}/logs/{branch_type}/.snakemake/log/").glob("*")
7653
)
77-
if len(logs) != 1:
54+
if len(logs) > 1:
7855
msg = (
79-
"Expected exactly one log file in snakemake/log directory "
80-
"({branch_type} branch)."
56+
"Expected exactly one log fiie in snakemake/log directory "
57+
f"({branch_type} branch). Found {len(logs)}."
8158
)
8259
raise ValueError(msg)
60+
elif len(logs) == 0:
61+
inpt_erros = ['no_logs_found']
62+
return inpt_erros
8363

8464
with logs[0].open() as file:
8565
log = file.read()
@@ -150,7 +130,7 @@ def create_details_block(summary: str, content: str) -> str:
150130
return ""
151131

152132

153-
class RunSuccessfull(CommentData):
133+
class RunSuccessfullComponent(CommentData):
154134
"""Class to generate successfull run component."""
155135

156136
def __init__(self):
@@ -315,20 +295,22 @@ def _format_csvs_dir_files(self, df: pd.DataFrame) -> pd.DataFrame:
315295
# Set header
316296
df.columns = df.iloc[header_row_index]
317297
# Fill nan values in header
318-
df.columns = [
319-
f"col{i+1}" if pd.isna(col) or col == "" or col is None else col
320-
for i, col in enumerate(df.columns)
321-
]
298+
df.columns = pd.Index(
299+
[
300+
f"col{i+1}" if pd.isna(col) or col == "" or col is None else col
301+
for i, col in enumerate(df.columns)
302+
]
303+
)
322304
# Remove all rows before header
323305
df = df.iloc[header_row_index + 1 :]
324306

325307
# Make non-numeric values the index
326308
non_numeric = df.apply(
327-
lambda col: pd.to_numeric(col, errors="coerce").isna().all()
309+
lambda col: pd.to_numeric(col, errors="coerce").isna().all() # type: ignore
328310
)
329311

330312
if non_numeric.any():
331-
df = df.set_index(df.columns[non_numeric].to_list())
313+
df = df.set_index(df.columns[non_numeric].to_list()) # type: ignore
332314
else:
333315
df = df.set_index("planning_horizon")
334316

@@ -507,7 +489,7 @@ def __call__(self) -> str:
507489
return self.body
508490

509491

510-
class RunFailed(CommentData):
492+
class RunFailedComponent(CommentData):
511493
"""Class to generate failed run component."""
512494

513495
def __init__(self):
@@ -545,7 +527,7 @@ def __call__(self) -> str:
545527
return self.body()
546528

547529

548-
class ModelMetrics(CommentData):
530+
class ModelMetricsComponent(CommentData):
549531
"""Class to generate model metrics component."""
550532

551533
def __init__(self):
@@ -575,140 +557,3 @@ def body(self) -> str:
575557
def __call__(self) -> str:
576558
"""Return text for model metrics component."""
577559
return self.body()
578-
579-
580-
class Comment(CommentData):
581-
"""Class to generate pypsa validator comment for GitHub PRs."""
582-
583-
def __init__(self) -> None:
584-
"""Initialize comment class. It will put all text components together."""
585-
super().__init__()
586-
587-
@property
588-
def header(self) -> str:
589-
"""
590-
Header text.
591-
592-
Contains the title, identifier, and short description.
593-
"""
594-
return (
595-
f""
596-
f"<!-- _val-bot-id-keyword_ -->\n"
597-
f"## Validator Report\n"
598-
f"I am the Validator. Download all artifacts [here](https://github.com/"
599-
f"{self.github_repository}/actions/runs/{self.github_run_id}).\n"
600-
f"I'll be back and edit this comment for each new commit.\n\n"
601-
)
602-
603-
@property
604-
def config_diff(self) -> str:
605-
"""
606-
Config diff text.
607-
608-
Only use when there are changes in the config.
609-
"""
610-
return (
611-
f"<details>\n"
612-
f" <summary>:warning: Config changes detected!</summary>\n"
613-
f"\n"
614-
f"Results may differ due to these changes:\n"
615-
f"```diff\n"
616-
f"{self.git_diff_config}\n"
617-
f"```\n"
618-
f"</details>\n\n"
619-
)
620-
621-
@property
622-
def subtext(self) -> str:
623-
"""Subtext for the comment."""
624-
if self.hash_feature:
625-
hash_feature = (
626-
f"([{self.hash_feature[:7]}](https://github.com/"
627-
f"{self.github_repository}/commits/{self.hash_feature})) "
628-
)
629-
if self.hash_main:
630-
hash_main = (
631-
f"([{self.hash_main[:7]}](https://github.com/"
632-
f"{self.github_repository}/commits/{self.hash_main}))"
633-
)
634-
time = (
635-
pd.Timestamp.now()
636-
.tz_localize("UTC")
637-
.tz_convert("Europe/Berlin")
638-
.strftime("%Y-%m-%d %H:%M:%S %Z")
639-
)
640-
return (
641-
f"Comparing `{self.github_head_ref}` {hash_feature}with "
642-
f"`{self.github_base_ref}` {hash_main}.\n"
643-
f"Branch is {self.ahead_count} commits ahead and {self.behind_count} "
644-
f"commits behind.\n"
645-
f"Last updated on `{time}`."
646-
)
647-
648-
def dynamic_plots(self) -> str:
649-
"""
650-
Return a list of dynamic results plots needed for the comment.
651-
652-
Returns
653-
-------
654-
str: Space separated list of dynamic plots.
655-
656-
"""
657-
if self.sucessfull_run:
658-
body_sucessfull = RunSuccessfull()
659-
plots_string = " ".join(body_sucessfull.variables_plot_strings)
660-
return plots_string
661-
else:
662-
""
663-
664-
def __repr__(self) -> str:
665-
"""Return full formatted comment."""
666-
body_benchmarks = ModelMetrics()
667-
if self.sucessfull_run:
668-
body_sucessfull = RunSuccessfull()
669-
670-
return (
671-
f"{self.header}"
672-
f"{self.config_diff if self.git_diff_config else ''}"
673-
f"{body_sucessfull()}"
674-
f"{body_benchmarks()}"
675-
f"{self.subtext}"
676-
)
677-
678-
else:
679-
body_failed = RunFailed()
680-
681-
return (
682-
f"{self.header}"
683-
f"{body_failed()}"
684-
f"{self.config_diff if self.git_diff_config else ''}"
685-
f"{body_benchmarks()}"
686-
f"{self.subtext}"
687-
)
688-
689-
690-
def main():
691-
"""
692-
Run draft comment script.
693-
694-
Command line interface for the draft comment script. Use no arguments to print the
695-
comment, or use the "plots" argument to print the dynamic plots which will be needed
696-
for the comment.
697-
"""
698-
parser = argparse.ArgumentParser(description="Process some comments.")
699-
parser.add_argument(
700-
"command", nargs="?", default="", help='Command to run, e.g., "plots".'
701-
)
702-
args = parser.parse_args()
703-
704-
comment = Comment()
705-
706-
if args.command == "plots":
707-
print(comment.dynamic_plots())
708-
709-
else:
710-
print(comment) # noqa T201
711-
712-
713-
if __name__ == "__main__":
714-
main()

0 commit comments

Comments
 (0)