Skip to content
Open
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
20 changes: 18 additions & 2 deletions pr_agent/algo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ def unique_strings(input_list: List[str]) -> List[str]:
return unique_list


def _expand_minute_suffix(text: str) -> str:
"""Replace minute abbreviations like '30m' with '30 minutes'.

Only replaces when 'm' appears at a word boundary after digits
(e.g. "30m" -> "30 minutes"), leaving partial-unit strings like
"30ms" or "30min" unchanged.
"""
return re.sub(r'(\d+)m\b', r'\1 minutes', text)


def convert_to_markdown_v2(output_data: dict,
gfm_supported: bool = True,
incremental_review=None,
Expand Down Expand Up @@ -214,11 +224,17 @@ def convert_to_markdown_v2(output_data: dict,
elif 'contribution time cost estimate' in key_nice.lower():
if gfm_supported:
markdown_text += f"<tr><td>{emoji}&nbsp;<strong>Contribution time estimate</strong> (best, average, worst case): "
markdown_text += f"{value['best_case'].replace('m', ' minutes')} | {value['average_case'].replace('m', ' minutes')} | {value['worst_case'].replace('m', ' minutes')}"
best = _expand_minute_suffix(value['best_case'])
avg = _expand_minute_suffix(value['average_case'])
worst = _expand_minute_suffix(value['worst_case'])
markdown_text += f"{best} | {avg} | {worst}"
markdown_text += f"</td></tr>\n"
else:
markdown_text += f"### {emoji} Contribution time estimate (best, average, worst case): "
markdown_text += f"{value['best_case'].replace('m', ' minutes')} | {value['average_case'].replace('m', ' minutes')} | {value['worst_case'].replace('m', ' minutes')}\n\n"
best = _expand_minute_suffix(value['best_case'])
avg = _expand_minute_suffix(value['average_case'])
worst = _expand_minute_suffix(value['worst_case'])
markdown_text += f"{best} | {avg} | {worst}\n\n"
elif 'security concerns' in key_nice.lower():
if gfm_supported:
markdown_text += f"<tr><td>"
Expand Down
22 changes: 21 additions & 1 deletion tests/unittest/test_convert_to_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import textwrap
from unittest.mock import Mock

from pr_agent.algo.utils import PRReviewHeader, convert_to_markdown_v2
from pr_agent.algo.utils import PRReviewHeader, _expand_minute_suffix, convert_to_markdown_v2
from pr_agent.tools.pr_description import insert_br_after_x_chars

"""
Expand Down Expand Up @@ -303,3 +303,23 @@ def test_br3(self):
'</code> and implements <br>aaa')
# print("-----")
# print(file_change_description_br)


class TestExpandMinuteSuffix:
"""Tests for _expand_minute_suffix regex replacement."""

def test_standalone_minute_suffix(self):
"""'30m' at end of string becomes '30 minutes'."""
assert _expand_minute_suffix("30m") == "30 minutes"

def test_minute_suffix_not_replaced_when_part_of_longer_unit(self):
"""'30ms' stays unchanged because 'm' is not at a word boundary."""
assert _expand_minute_suffix("30ms") == "30ms"

def test_minute_suffix_replaced_before_space(self):
"""'30m implementation' replaces '30m' because 'm' is at a word boundary."""
assert _expand_minute_suffix("30m implementation") == "30 minutes implementation"

def test_minute_suffix_in_compound_estimate(self):
"""'2h 30m' becomes '2h 30 minutes' (only the minute part is replaced)."""
assert _expand_minute_suffix("2h 30m") == "2h 30 minutes"
Loading