Skip to content

Commit 7a02a1e

Browse files
committed
timestamp consistency
1 parent 9e33c13 commit 7a02a1e

File tree

5 files changed

+289
-25
lines changed

5 files changed

+289
-25
lines changed

.github/workflows/pylint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ jobs:
6464
python src/rattlesnake/cicd/report_pylint.py \
6565
--input_file ${{ env.PYLINT_OUTPUT_FILE }} \
6666
--output_file pylint_report.html \
67+
--timestamp ${{ env.TIMESTAMP }} \
6768
--run_id ${{ github.run_id }} \
6869
--ref_name ${{ github.ref_name }} \
6970
--github_sha ${{ github.sha }} \

src/rattlesnake/cicd/report_pylint.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# import pytz # unused import
1717

18-
from rattlesnake.cicd.utilities import get_score_color, get_timestamp
18+
from rattlesnake.cicd.utilities import get_score_color, extend_timestamp
1919

2020

2121
# def get_timestamp() -> str:
@@ -180,6 +180,7 @@ def get_report_html(
180180
issues: List[str],
181181
summary_lines: List[str],
182182
pylint_score: str,
183+
timestamp: str,
183184
run_id: str,
184185
ref_name: str,
185186
github_sha: str,
@@ -201,7 +202,8 @@ def get_report_html(
201202
Returns:
202203
Complete HTML report as string
203204
"""
204-
timestamp: str = get_timestamp()
205+
# timestamp: str = get_timestamp()
206+
timestamp_ext: str = extend_timestamp(short=timestamp)
205207
score_color: str = get_score_color(pylint_score)
206208
issue_counts: Dict[str, int] = get_issue_counts(issues)
207209
issues_html: str = get_issues_list_html(issues)
@@ -286,7 +288,7 @@ def get_report_html(
286288
<h1>Rattlesnake Pylint Report</h1>
287289
<div class="score">{pylint_score}/10</div>
288290
<div class="metadata">
289-
<div><strong>Generated:</strong> {timestamp}</div>
291+
<div><strong>Generated:</strong> {timestamp_ext}</div>
290292
<div><strong>Run ID:</strong> <a href="https://github.com/{github_repo}/actions/runs/{run_id}"> {run_id}</a></div>
291293
<div><strong>Branch:</strong> <a href="https://github.com/{github_repo}/tree/{ref_name}"> {ref_name}</a></div>
292294
<div><strong>Commit:</strong> <a href="https://github.com/{github_repo}/commit/{github_sha}"> {github_sha[:7]}</a></div>
@@ -365,6 +367,7 @@ def write_report(html_content: str, output_file: str) -> None:
365367
def run_pylint_report(
366368
input_file: str,
367369
output_file: str,
370+
timestamp: str,
368371
run_id: str,
369372
ref_name: str,
370373
github_sha: str,
@@ -376,6 +379,8 @@ def run_pylint_report(
376379
Args:
377380
input_file: Path to the pylint output text file
378381
output_file: Path for the generated HTML report
382+
timestamp: The timestampe from bash when pylint ran, in format YYYYMMDD_HHMMSS_UTC
383+
e.g., 20250815_211112_UTC
379384
run_id: GitHub Actions run ID
380385
ref_name: Git reference name (branch)
381386
github_sha: GitHub commit SHA
@@ -399,6 +404,7 @@ def run_pylint_report(
399404
issues,
400405
summary_lines,
401406
pylint_score,
407+
timestamp,
402408
run_id,
403409
ref_name,
404410
github_sha,
@@ -429,6 +435,7 @@ def parse_arguments() -> argparse.Namespace:
429435
python pylint_report.py \
430436
--input_file pylint_output_20240101_120000_UTC.txt \
431437
--output_file pylint_report.html \
438+
--timestamp 20240101_120000_UTC \
432439
--run_id 1234567890 \
433440
--ref_name main \
434441
--github_sha abc123def456 \
@@ -440,6 +447,10 @@ def parse_arguments() -> argparse.Namespace:
440447

441448
parser.add_argument("--output_file", required=True, help="Output HTML report file")
442449

450+
parser.add_argument(
451+
"--timestamp", required=True, help="UTC timestamp, e.g., 20240101_120000_UTC"
452+
)
453+
443454
parser.add_argument("--run_id", required=True, help="GitHub Actions run ID")
444455

445456
parser.add_argument(
@@ -468,6 +479,7 @@ def main() -> int:
468479
total_issues, issue_counts, pylint_score = run_pylint_report(
469480
args.input_file,
470481
args.output_file,
482+
args.timestamp,
471483
args.run_id,
472484
args.ref_name,
473485
args.github_sha,

src/rattlesnake/cicd/utilities.py

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from datetime import datetime
77
import pytz
8+
import re
89

910

1011
def get_score_color(pylint_score: str) -> str:
@@ -75,12 +76,28 @@ def extend_timestamp(short: str) -> str:
7576
Extended timestamp, for example
7677
2025-08-15 21:11:12 UTC (2025-08-15 17:11:12 EST / 2025-08-15 15:11:12 MST)
7778
"""
78-
# The format of the input timestamp
79+
# Regex pattern to match the required format: YYYYMMDD_HHMMSS_TZ
80+
# The timezone abbreviation must be 'UTC', 'GMT', or 'Z'
81+
pattern: re.Pattern = re.compile(r"^(\d{8})_(\d{6})_(UTC|GMT|Z)$")
82+
match = pattern.match(short)
83+
84+
if not match:
85+
raise ValueError(
86+
f"Invalid timestamp format: '{short}'. "
87+
f"Expected format is YYYYMMDD_HHMMSS_TZ, where TZ is one of UTC, GMT, or Z."
88+
)
89+
90+
# Extract the date and time parts from the regex match
91+
date_part, time_part, _ = match.groups()
92+
93+
# Combine the parts into a format that can be parsed by datetime
94+
datetime_str: str = f"{date_part}_{time_part}_UTC"
7995
input_format: str = "%Y%m%d_%H%M%S_%Z"
8096

8197
# Convert the input string to a datetime object
82-
utc_datetime: datetime = datetime.strptime(short, input_format)
83-
# The datetime object is naive, so we make it timezone-aware
98+
utc_datetime: datetime = datetime.strptime(datetime_str, input_format)
99+
100+
# Make the datetime object timezone-aware
84101
utc_now: datetime = pytz.utc.localize(utc_datetime)
85102

86103
# Define the time zones
@@ -92,12 +109,71 @@ def extend_timestamp(short: str) -> str:
92109
mst_now: datetime = utc_now.astimezone(timezone_mst)
93110

94111
# Format the output
95-
df: str = "%Y-%m-%d %H:%M:%S " # Date format
96-
utc: str = utc_now.strftime(df + "UTC")
97-
est: str = est_now.strftime(df + "EST")
98-
mst: str = mst_now.strftime(df + "MST")
112+
df: str = "%Y-%m-%d %H:%M:%S" # Date format without trailing space
113+
utc: str = utc_now.strftime(df)
114+
est: str = est_now.strftime(df)
115+
mst: str = mst_now.strftime(df)
116+
117+
# Use timezone abbreviations from the datetime objects
118+
utc_tz_abbr: str = utc_now.strftime("%Z")
119+
# est_tz_abbr: str = est_now.strftime("%Z")
120+
est_tz_abbr: str = "EST"
121+
# mst_tz_abbr: str = mst_now.strftime("%Z")
122+
mst_tz_abbr: str = "MST"
99123

100124
# Combine the formatted times
101-
timestamp: str = f"{utc} ({est} / {mst})"
125+
timestamp: str = f"{utc} {utc_tz_abbr} ({est} {est_tz_abbr} / {mst} {mst_tz_abbr})"
102126

103127
return timestamp
128+
129+
# try:
130+
# # The format of the input timestamp
131+
# # Use a flexible format to parse a naive datetime object first
132+
# input_format_naive: str = "%Y%m%d_%H%M%S"
133+
134+
# # Separate the datetime part and the timezone abbreviation
135+
# dt_part, tz_part = short.rsplit("_", 1)
136+
137+
# # Handle different UTC-like timezone abbreviations
138+
# if tz_part not in ["UTC", "GMT", "Z"]:
139+
# # Raise an error if the timezone is not recognized
140+
# raise ValueError(f"Unrecognized timezone abbreviation: {tz_part}")
141+
142+
# # Convert the input string to a naive datetime object
143+
# utc_datetime: datetime = datetime.strptime(dt_part, input_format_naive)
144+
145+
# # Make the datetime object timezone-aware by localizing to UTC
146+
# utc_now: datetime = pytz.utc.localize(utc_datetime)
147+
148+
# # Define the time zones
149+
# timezone_est: pytz.BaseTzInfo = pytz.timezone("America/New_York")
150+
# timezone_mst: pytz.BaseTzInfo = pytz.timezone("America/Denver")
151+
152+
# # Convert UTC time to EST and MST
153+
# est_now: datetime = utc_now.astimezone(timezone_est)
154+
# mst_now: datetime = utc_now.astimezone(timezone_mst)
155+
156+
# # Format the output
157+
# df: str = "%Y-%m-%d %H:%M:%S" # Date format without trailing space
158+
# utc: str = utc_now.strftime(df)
159+
# est: str = est_now.strftime(df)
160+
# mst: str = mst_now.strftime(df)
161+
162+
# # Use timezone abbreviations from the datetime objects
163+
# utc_tz_abbr: str = utc_now.strftime("%Z")
164+
# est_tz_abbr: str = est_now.strftime("%Z")
165+
# mst_tz_abbr: str = mst_now.strftime("%Z")
166+
167+
# # Combine the formatted times
168+
# timestamp: str = (
169+
# f"{utc} {utc_tz_abbr} ({est} {est_tz_abbr} / {mst} {mst_tz_abbr})"
170+
# )
171+
172+
# return timestamp
173+
174+
# except (ValueError, IndexError) as e:
175+
# # Catch errors from strptime or rsplit and raise a more descriptive ValueError
176+
# raise ValueError(
177+
# f"Invalid timestamp format: '{short}'. "
178+
# f"Expected format is YYYYMMDD_HHMMSS_UTC, YYYYMMDD_HHMMSS_GMT, or YYYYMMDD_HHMMSS_Z."
179+
# ) from e

tests/test_report_pylint.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
get_pylint_sections,
2626
get_report_html,
2727
get_score_from_summary,
28-
get_timestamp,
2928
main,
3029
run_pylint_report,
3130
write_report,
@@ -96,6 +95,7 @@ def test_main_success(monkeypatch, capsys):
9695
mock_args = types.SimpleNamespace(
9796
input_file="dummy_input.txt",
9897
output_file="dummy_output.html",
98+
timestamp="20240101_120000_UTC",
9999
run_id="123",
100100
ref_name="main",
101101
github_sha="abc",
@@ -127,6 +127,7 @@ def test_main_file_not_found(monkeypatch, capsys):
127127
mock_args = types.SimpleNamespace(
128128
input_file="non_existent.txt",
129129
output_file="dummy_output.html",
130+
timestamp="20240101_120000_UTC",
130131
run_id="123",
131132
ref_name="main",
132133
github_sha="abc",
@@ -151,6 +152,7 @@ def test_main_io_error(monkeypatch, capsys):
151152
mock_args = types.SimpleNamespace(
152153
input_file="dummy_input.txt",
153154
output_file="dummy_output.html",
155+
timestamp="20240101_120000_UTC",
154156
run_id="123",
155157
ref_name="main",
156158
github_sha="abc",
@@ -175,6 +177,7 @@ def test_main_unexpected_error(monkeypatch, capsys):
175177
mock_args = types.SimpleNamespace(
176178
input_file="dummy_input.txt",
177179
output_file="dummy_output.html",
180+
timestamp="20240101_120000_UTC",
178181
run_id="123",
179182
ref_name="main",
180183
github_sha="abc",
@@ -211,19 +214,19 @@ def test_get_score_color():
211214
assert get_score_color("") == "gray", "Test failed for empty string"
212215

213216

214-
def test_get_timestamp():
215-
"""Test that formatted_timestamp() returns valid datetime string.
216-
217-
Confirms output includes expected format components like UTC, EST, MST,
218-
and a recognizable timestamp pattern.
219-
220-
Example get_formatted_timestamp output:
221-
'2025-07-31 18:33:56 UTC (2025-07-31 14:33:56 EST / 2025-07-31 12:33:56 MST)'
222-
"""
223-
ts = get_timestamp()
224-
225-
assert "UTC" in ts
226-
assert re.search(r"\d{4}-\d{2}-\d{2}", ts)
217+
# def test_get_timestamp_ext():
218+
# """Test that formatted_timestamp() returns valid datetime string.
219+
#
220+
# Confirms output includes expected format components like UTC, EST, MST,
221+
# and a recognizable timestamp pattern.
222+
#
223+
# Example get_formatted_timestamp output:
224+
# '2025-07-31 18:33:56 UTC (2025-07-31 14:33:56 EST / 2025-07-31 12:33:56 MST)'
225+
# """
226+
# ts = get_timestamp()
227+
#
228+
# assert "UTC" in ts
229+
# assert re.search(r"\d{4}-\d{2}-\d{2}", ts)
227230

228231

229232
def test_get_issue_counts():
@@ -326,6 +329,7 @@ def test_get_report_html():
326329
issues=issues,
327330
summary_lines=summary,
328331
pylint_score="9.00",
332+
timestamp="20250815_211112_UTC",
329333
run_id="123",
330334
ref_name="main",
331335
github_sha="abc123def456",
@@ -355,6 +359,7 @@ def test_run_pylint_report():
355359
_total_issues, _issue_counts, _pylint_score = run_pylint_report(
356360
input_file=str(fin),
357361
output_file=str(fout),
362+
timestamp="20250815_211112_UTC",
358363
run_id="1234567890",
359364
ref_name="main",
360365
github_sha="abc123def456",

0 commit comments

Comments
 (0)