diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index d5e1a3c63..8b2f21d79 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -59,14 +59,14 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: if key.lower() == 'code feedback': if gfm_supported: markdown_text += f"\n\n- " - markdown_text += f"
{ emoji } Code feedback:\n\n" + markdown_text += f"
{ emoji } Code feedback:" else: markdown_text += f"\n\n- **{emoji} Code feedback:**\n\n" else: markdown_text += f"- {emoji} **{key}:**\n\n" - for item in value: + for i, item in enumerate(value): if isinstance(item, dict) and key.lower() == 'code feedback': - markdown_text += parse_code_suggestion(item, gfm_supported) + markdown_text += parse_code_suggestion(item, i, gfm_supported) elif item: markdown_text += f" - {item}\n" if key.lower() == 'code feedback': @@ -80,7 +80,7 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: return markdown_text -def parse_code_suggestion(code_suggestions: dict, gfm_supported: bool=True) -> str: +def parse_code_suggestion(code_suggestions: dict, i: int = 0, gfm_supported: bool = True) -> str: """ Convert a dictionary of data into markdown format. @@ -91,24 +91,52 @@ def parse_code_suggestion(code_suggestions: dict, gfm_supported: bool=True) -> s str: A string containing the markdown formatted text generated from the input dictionary. """ markdown_text = "" - for sub_key, sub_value in code_suggestions.items(): - if isinstance(sub_value, dict): # "code example" - markdown_text += f" - **{sub_key}:**\n" - for code_key, code_value in sub_value.items(): # 'before' and 'after' code - code_str = f"```\n{code_value}\n```" - code_str_indented = textwrap.indent(code_str, ' ') - markdown_text += f" - **{code_key}:**\n{code_str_indented}\n" - else: - if "relevant file" in sub_key.lower(): - markdown_text += f"\n - **{sub_key}:** {sub_value} \n" + if gfm_supported and 'relevant line' in code_suggestions: + if i == 0: + markdown_text += "
" + markdown_text += '' + for sub_key, sub_value in code_suggestions.items(): + try: + if sub_key.lower() == 'relevant file': + relevant_file = sub_value.strip('`').strip('"').strip("'") + markdown_text += f"" + # continue + elif sub_key.lower() == 'suggestion': + markdown_text += f"" + elif sub_key.lower() == 'relevant line': + markdown_text += f"" + sub_value_list = sub_value.split('](') + relevant_line = sub_value_list[0].lstrip('`').lstrip('[') + if len(sub_value_list) > 1: + link = sub_value_list[1].rstrip(')').strip('`') + markdown_text += f"" + else: + markdown_text += f"" + markdown_text += "" + except Exception as e: + get_logger().exception(f"Failed to parse code suggestion: {e}") + pass + markdown_text += '
{sub_key}{relevant_file}
{sub_key}      {sub_value}
relevant line{relevant_line}{relevant_line}
' + markdown_text += "
" + else: + for sub_key, sub_value in code_suggestions.items(): + if isinstance(sub_value, dict): # "code example" + markdown_text += f" - **{sub_key}:**\n" + for code_key, code_value in sub_value.items(): # 'before' and 'after' code + code_str = f"```\n{code_value}\n```" + code_str_indented = textwrap.indent(code_str, ' ') + markdown_text += f" - **{code_key}:**\n{code_str_indented}\n" else: - markdown_text += f" **{sub_key}:** {sub_value} \n" - if not gfm_supported: - if "relevant line" not in sub_key.lower(): # nicer presentation + if "relevant file" in sub_key.lower(): + markdown_text += f"\n - **{sub_key}:** {sub_value} \n" + else: + markdown_text += f" **{sub_key}:** {sub_value} \n" + if not gfm_supported: + if "relevant line" not in sub_key.lower(): # nicer presentation # markdown_text = markdown_text.rstrip('\n') + "\\\n" # works for gitlab markdown_text = markdown_text.rstrip('\n') + " \n" # works for gitlab and bitbucker - markdown_text += "\n" + markdown_text += "\n" return markdown_text diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 7b11ef54e..c5e77d07c 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -211,7 +211,11 @@ def send_inline_comment(self,body: str,edit_type: str,found: bool,relevant_file: pos_obj['new_line'] = target_line_no - 1 pos_obj['old_line'] = source_line_no - 1 get_logger().debug(f"Creating comment in {self.id_mr} with body {body} and position {pos_obj}") - self.mr.discussions.create({'body': body, 'position': pos_obj}) + try: + self.mr.discussions.create({'body': body, 'position': pos_obj}) + except Exception as e: + get_logger().debug( + f"Failed to create comment in {self.id_mr} with position {pos_obj} (probably not a '+' line)") def get_relevant_diff(self, relevant_file: str, relevant_line_in_file: int) -> Optional[dict]: changes = self.mr.changes() # Retrieve the changes for the merge request once diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py index b03c4fdec..800d3ada8 100644 --- a/tests/unittest/test_convert_to_markdown.py +++ b/tests/unittest/test_convert_to_markdown.py @@ -71,7 +71,7 @@ def test_simple_dictionary_input(self): - ๐Ÿ“Œ **Type of PR:** Test type\n\ - ๐Ÿงช **Relevant tests added:** no\n\ - โœจ **Focused PR:** Yes\n\ -- **General PR suggestions:** general suggestion...\n\n\n-
๐Ÿค– Code feedback:\n\n - **Code example:**\n - **Before:**\n ```\n Code before\n ```\n - **After:**\n ```\n Code after\n ```\n\n - **Code example:**\n - **Before:**\n ```\n Code before 2\n ```\n - **After:**\n ```\n Code after 2\n ```\n\n
\ +- **General PR suggestions:** general suggestion...\n\n\n-
๐Ÿค– Code feedback: - **Code example:**\n - **Before:**\n ```\n Code before\n ```\n - **After:**\n ```\n Code after\n ```\n\n - **Code example:**\n - **Before:**\n ```\n Code before 2\n ```\n - **After:**\n ```\n Code after 2\n ```\n\n
\ """ assert convert_to_markdown(input_data).strip() == expected_output.strip()