Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hl/pr review table #646

Merged
merged 14 commits into from
Feb 9, 2024
1 change: 0 additions & 1 deletion docs/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ To edit [configurations](./../pr_agent/settings/configuration.toml#L19) related
- `require_focused_review`: if set to true, the tool will add a section - 'is the PR a focused one'. Default is false.
- `require_score_review`: if set to true, the tool will add a section that scores the PR. Default is false.
- `require_tests_review`: if set to true, the tool will add a section that checks if the PR contains tests. Default is true.
- `require_security_review`: if set to true, the tool will add a section that checks if the PR contains security issues. Default is true.
- `require_estimate_effort_to_review`: if set to true, the tool will add a section that estimates the effort needed to review the PR. Default is true.
#### SOC2 ticket compliance 💎
This sub-tool checks if the PR description properly contains a ticket to a project management system (e.g., Jira, Asana, Trello, etc.), as required by SOC2 compliance. If not, it will add a label to the PR: "Missing SOC2 ticket".
Expand Down
95 changes: 43 additions & 52 deletions pr_agent/algo/utils.py
mrT23 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -36,86 +36,77 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str:
Returns:
str: The markdown formatted text generated from the input dictionary.
"""
markdown_text = ""

emojis = {
"Main theme": "🎯",
"PR summary": "📝",
"Type of PR": "📌",
"Possible issues": "🔍",
"Score": "🏅",
"Relevant tests added": "🧪",
"Unrelated changes": "⚠️",
"Focused PR": "✨",
"Security concerns": "🔒",
"General suggestions": "💡",
"Insights from user's answers": "📝",
"Code feedback": "🤖",
"Estimated effort to review [1-5]": "⏱️",
}
markdown_text = ""
markdown_text += f"## PR Review\n\n"
markdown_text += "<table>\n<tr>\n"
markdown_text += """<td> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>PR&nbsp;feedback</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td> <td></td></tr>"""

if not output_data or not output_data.get('review', {}):
return ""

for key, value in output_data.items():
for key, value in output_data['review'].items():
if value is None or value == '' or value == {} or value == []:
continue
if isinstance(value, dict):
markdown_text += f"## {key}\n\n"
markdown_text += convert_to_markdown(value, gfm_supported)
elif isinstance(value, list):
emoji = emojis.get(key, "")
if key.lower() == 'code feedback':
if gfm_supported:
markdown_text += f"\n\n"
markdown_text += f"<details><summary> <strong>{ emoji } Code feedback:</strong></summary>"
else:
markdown_text += f"\n\n**{emoji} Code feedback:**\n\n"
else:
markdown_text += f"- {emoji} **{key}:**\n\n"
for i, item in enumerate(value):
if isinstance(item, dict) and key.lower() == 'code feedback':
markdown_text += parse_code_suggestion(item, i, gfm_supported)
elif item:
markdown_text += f" - {item}\n"
if key.lower() == 'code feedback':
if gfm_supported:
markdown_text += "</details>\n\n"
else:
markdown_text += "\n\n"
elif value != 'n/a':
emoji = emojis.get(key, "")
if key.lower() == 'general suggestions':
if gfm_supported:
markdown_text += f"\n\n<strong>{emoji} General suggestions:</strong> {value}\n"
else:
markdown_text += f"{emoji} **General suggestions:** {value}\n"
else:
markdown_text += f"- {emoji} **{key}:** {value}\n"
key_nice = key.replace('_', ' ').capitalize()
emoji = emojis.get(key_nice, "")
markdown_text += f"<tr><td> {emoji} {key_nice}</td><td>\n\n{value}\n\n</td></tr>\n"
markdown_text += "</table>\n"

if 'code_feedback' in output_data:
if gfm_supported:
markdown_text += f"\n\n"
markdown_text += f"<details><summary> <strong>Code feedback:</strong></summary>\n\n"
else:
markdown_text += f"\n\n** Code feedback:**\n\n"
markdown_text += "<hr>"
for i, value in enumerate(output_data['code_feedback']):
if value is None or value == '' or value == {} or value == []:
continue
markdown_text += parse_code_suggestion(value, i, gfm_supported)+"\n\n"
if markdown_text.endswith('<hr>'):
markdown_text = markdown_text[:-4]
if gfm_supported:
markdown_text += f"</details>"
#print(markdown_text)


return markdown_text


def parse_code_suggestion(code_suggestions: dict, i: int = 0, gfm_supported: bool = True) -> str:
def parse_code_suggestion(code_suggestion: dict, i: int = 0, gfm_supported: bool = True) -> str:
"""
Convert a dictionary of data into markdown format.

Args:
code_suggestions (dict): A dictionary containing data to be converted to markdown format.
code_suggestion (dict): A dictionary containing data to be converted to markdown format.

Returns:
str: A string containing the markdown formatted text generated from the input dictionary.
"""
markdown_text = ""
if gfm_supported and 'relevant line' in code_suggestions:
if i == 0:
markdown_text += "<hr>"
if gfm_supported and 'relevant_line' in code_suggestion:
markdown_text += '<table>'
for sub_key, sub_value in code_suggestions.items():
for sub_key, sub_value in code_suggestion.items():
try:
if sub_key.lower() == 'relevant file':
if sub_key.lower() == 'relevant_file':
relevant_file = sub_value.strip('`').strip('"').strip("'")
markdown_text += f"<tr><td>{sub_key}</td><td>{relevant_file}</td></tr>"
markdown_text += f"<tr><td>relevant file</td><td>{relevant_file}</td></tr>"
# continue
elif sub_key.lower() == 'suggestion':
markdown_text += (f"<tr><td>{sub_key} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>"
f"<td><br>\n\n**{sub_value.strip()}**\n<br></td></tr>")
elif sub_key.lower() == 'relevant line':
f"<td>\n\n<strong>\n\n{sub_value.strip()}\n\n</strong>\n</td></tr>")
elif sub_key.lower() == 'relevant_line':
markdown_text += f"<tr><td>relevant line</td>"
sub_value_list = sub_value.split('](')
relevant_line = sub_value_list[0].lstrip('`').lstrip('[')
Expand All @@ -131,20 +122,20 @@ def parse_code_suggestion(code_suggestions: dict, i: int = 0, gfm_supported: boo
markdown_text += '</table>'
markdown_text += "<hr>"
else:
for sub_key, sub_value in code_suggestions.items():
for sub_key, sub_value in code_suggestion.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():
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
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

Expand Down
4 changes: 2 additions & 2 deletions pr_agent/git_providers/bitbucket_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,8 @@ def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_l

def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant file'].strip('`').strip("'")
relevant_line_str = suggestion['relevant line']
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""

Expand Down
4 changes: 2 additions & 2 deletions pr_agent/git_providers/bitbucket_server_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ def publish_inline_comment(self, comment: str, from_line: int, file: str):

def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant file'].strip('`').strip("'")
relevant_line_str = suggestion['relevant line']
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""

Expand Down
4 changes: 2 additions & 2 deletions pr_agent/git_providers/github_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,8 +603,8 @@ def get_commit_messages(self):

def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant file'].strip('`').strip("'")
relevant_line_str = suggestion['relevant line']
relevant_file = suggestion['relevant_file'].strip('`').strip("'").strip('\n')
relevant_line_str = suggestion['relevant_line'].strip('\n')
if not relevant_line_str:
return ""

Expand Down
4 changes: 2 additions & 2 deletions pr_agent/git_providers/gitlab_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,8 @@ def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_l

def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant file'].strip('`').strip("'")
relevant_line_str = suggestion['relevant line']
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""

Expand Down
2 changes: 1 addition & 1 deletion pr_agent/servers/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get_review_usage_guide():
```
[pr_reviewer] # /review #
extra_instructions="""
In the 'general suggestions' section, emphasize the following:
In the 'possible issues' section, emphasize the following:
- Does the code logic cover relevant edge cases?
- Is the code logic clear and easy to understand?
- Is the code logic efficient?
Expand Down
4 changes: 1 addition & 3 deletions pr_agent/settings/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ verbosity_level=0 # 0,1,2
use_extra_bad_extensions=false
use_repo_settings_file=true
use_global_settings_file=true
ai_timeout=90
ai_timeout=120 # 2minutes
max_description_tokens = 500
max_commits_tokens = 500
max_model_tokens = 32000 # Limits the maximum number of tokens that can be used by any model, regardless of the model's default capabilities.
Expand All @@ -22,7 +22,6 @@ cli_mode=false
require_focused_review=false
require_score_review=false
require_tests_review=true
require_security_review=true
require_estimate_effort_to_review=true
# soc2
require_soc2_ticket=false
Expand Down Expand Up @@ -149,7 +148,6 @@ push_commands = [
--pr_reviewer.require_focused_review=false \
--pr_reviewer.require_score_review=false \
--pr_reviewer.require_tests_review=false \
--pr_reviewer.require_security_review=false \
--pr_reviewer.require_estimate_effort_to_review=false \
--pr_reviewer.num_code_suggestions=0 \
--pr_reviewer.inline_code_comments=false \
Expand Down
2 changes: 1 addition & 1 deletion pr_agent/settings/pr_description_prompts.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ labels:
{%- endif %}
```

Answer should be a valid YAML, and nothing else. Each YAML output MUST be after a newline, with proper indent, and block scalar indicator ('|-')
Answer should be a valid YAML, and nothing else. Each YAML output MUST be after a newline, with proper indent, and block scalar indicator ('|')
"""

user="""PR Info:
Expand Down
Loading
Loading