"""Error and notification messages for PR quality checks.
Message constants are plain 2-tuples of (title, body). Body strings use
str.format() placeholders; kwargs are supplied at Message() construction time.
"""
LEVEL_ERROR = ("🛑", "Error")
LEVEL_WARNING = ("⚠️", "Warning")
class Message:
"""A PR quality check message bound to its runtime formatting kwargs."""
def __init__(self, title, body, **kwargs):
self.title = title
self.body = body
self.kwargs = kwargs
def as_details(self, level=LEVEL_ERROR):
body = self.body.format(**self.kwargs) if self.kwargs else self.body
symbol, label = level
return (
f"\n{symbol} {label}: {self.title}"
f"
\n\n{body}\n\n "
)
CONTRIBUTING_URL = "https://docs.djangoproject.com/en/dev/internals/contributing/"
SUBMITTING_URL = f"{CONTRIBUTING_URL}writing-code/submitting-patches/"
TRIAGING_URL = f"{CONTRIBUTING_URL}triaging-tickets/"
FORUM_URL = "https://forum.djangoproject.com"
TRAC_URL = "https://code.djangoproject.com"
CHECKS_HEADER = (
"Thank you for your contribution to Django! This pull request has one or more "
"items that need attention before it can be accepted for review."
)
CHECKS_FOOTER = (
"If you have questions about these requirements, please review the "
f"[contributing guidelines]({SUBMITTING_URL}) or ask for help on the "
f"[Django Forum]({FORUM_URL}/c/internals/mentorship/10)."
)
INCOMPLETE_CHECKLIST = (
"Incomplete Checklist",
"The items in the **Checklist** section must be all understood and checked "
"accordingly before this PR can be accepted. These items confirm that you have "
f"read and followed the [Django contributing guidelines]({SUBMITTING_URL}).\n\n"
"**What to do:**\n\n"
"- Review each item and change `[ ]` to `[x]` once you have completed it.",
)
INVALID_TRAC_STATUS = (
"Trac Ticket Not Ready for a Pull Request",
"The referenced ticket **ticket-{ticket_id}** is not ready for a pull request. "
"A ticket must be in the *Accepted* stage, *assigned* status, and have no "
"resolution.\n\n"
"**Current state:** {current_state}\n\n"
"**What to do:**\n\n"
f"1. Check the ticket at {TRAC_URL}/ticket/{{ticket_id}}.\n"
"2. If *Unreviewed*, wait for a community member to accept it. "
"A ticket cannot be accepted by its reporter.\n"
"3. If *Ready for Checkin*, there is already a solution for it.\n"
"4. If *Someday/Maybe*, discuss on the "
f"[Django Forum]({FORUM_URL}/c/internals/5) before proceeding.\n"
"5. If resolved or closed, it is not eligible for a PR.\n"
"6. If not *assigned*, claim it by setting yourself as the owner.\n\n"
f"For more information on the Django triage process see {TRIAGING_URL}.",
)
MISSING_AI_DESCRIPTION = (
"AI Tool Usage Not Described",
"You indicated that AI tools were used in preparing this PR, but you have not "
"provided a description of which tools you used or how you used them. At least "
"5 words of description are required.\n\n"
"**What to do:**\n\n"
"Add a brief description below the checked AI disclosure checkbox in your PR "
"description. For example:\n\n"
"```\n"
"- [x] **If AI tools were used**, I have disclosed which ones, and fully reviewed "
"and verified their output.\n\n"
"Used GitHub Copilot for initial code generation. All output was manually "
"reviewed, tested, and verified for correctness.\n"
"```\n\n"
"Your description should cover:\n\n"
"- Which AI tool(s) and versions you used (e.g. GitHub Copilot, ChatGPT, Claude)\n"
"- How you used them (e.g. code generation, writing tests, drafting commit "
"messages)\n"
"- How you reviewed the generated result\n\n"
"Please note that you are fully responsible for the correctness and quality of all "
"content in this PR, regardless of how it was generated.",
)
MISSING_AI_DISCLOSURE = (
"AI Tool Usage Not Disclosed",
"You must select exactly one checkbox in the AI Assistance Disclosure section of "
"your PR description.\n\n"
"**What to do:**\n\n"
"Change `[ ]` to `[x]` for either:\n\n"
'1. "**No AI tools were used** in preparing this PR."\n\n'
"OR\n\n"
'2. "**If AI tools were used**, I have disclosed which ones, and fully reviewed '
'and verified their output."\n\n'
"**Key Requirements:**\n\n"
"- Select only one option\n"
"- Do not check both boxes\n"
"- Do not leave both unchecked\n"
"- If selecting the second option, provide details of which AI tools were used.",
)
MISSING_DESCRIPTION = (
"Missing PR Description",
"Your PR description must be substantive and meaningful. The placeholder text "
'"*Provide a concise overview of the issue or rationale behind the proposed '
'changes.*" is not acceptable.\n\n'
"**What to do:**\n\n"
"Write a description that contains at least 5 words and addresses:\n\n"
"- What problem does this PR solve?\n"
"- Why is this change necessary?\n"
"- What approach did you take?\n\n"
"A meaningful description helps reviewers understand the intent of your change "
"quickly and increases the likelihood that your PR will be reviewed promptly.",
)
MISSING_HAS_PATCH_FLAG = (
"Incorrect Trac Ticket Flag",
"The referenced ticket **ticket-{ticket_id}** does not have the *Has patch* "
"flag set in Trac. This flag must be checked before a pull request can be "
"reviewed.\n\n"
"**What to do:**\n\n"
f"1. Open the ticket at {TRAC_URL}/ticket/{{ticket_id}}.\n"
"2. Scroll down and check the *Has patch* checkbox on the ticket.\n"
"3. Save the ticket.\n\n"
f"For more information see {TRIAGING_URL}#has-patch.",
)
MISSING_TICKET_IN_PR_TITLE = (
"Ticket Number Missing from PR Title",
"The PR title does not include the ticket number **#{ticket_id}**. Including "
"it allows Trac to automatically link this pull request to the ticket.\n\n"
"**What to do:**\n\n"
"Edit the PR title to follow the commit message format, for example:\n\n"
"`Fixed #{ticket_id} -- .`",
)
MISSING_TRAC_TICKET = (
"Missing Trac Ticket",
"This PR does not include a valid Trac ticket reference.\n\n"
"**What to do:**\n\n"
f"1. Visit {TRAC_URL} and find or file the appropriate ticket for your change.\n"
"2. Wait for the ticket to be triaged and accepted before submitting a PR. "
"Patches submitted against unreviewed tickets are unlikely to be merged.\n"
"3. Edit the **Trac ticket number** section of your PR description to include "
"the ticket in the format `ticket-NNNNN` (e.g. `ticket-36991`).\n\n"
"For PRs with fewer than {threshold} lines changed (additions + deletions), you "
"may write `N/A` in the ticket field instead (e.g. `N/A - typo fix`).",
)