diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py
index 3e8576753f..414edd6553 100644
--- a/pr_agent/algo/utils.py
+++ b/pr_agent/algo/utils.py
@@ -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,
@@ -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"
| {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')}"
+ 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" |
\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""
diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py
index 0d18e03cec..980d735a58 100644
--- a/tests/unittest/test_convert_to_markdown.py
+++ b/tests/unittest/test_convert_to_markdown.py
@@ -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
"""
@@ -303,3 +303,23 @@ def test_br3(self):
' and implements 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"
|