from rest_framework.exceptions import ParseError
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from . import github_utils
class HealthcheckView(APIView):
"""
Provides a simple view to check that the website is alive and well.
## Routes
### GET /healthcheck
Returns a simple JSON document showcasing whether the system is working:
>>> {
... 'status': 'ok'
... }
Seems to be.
## Authentication
Does not require any authentication nor permissions.
"""
authentication_classes = ()
permission_classes = ()
def get(self, request, format=None): # noqa: D102,ANN001,ANN201
return Response({'status': 'ok'})
class RulesView(APIView):
"""
Return a list of the server's rules.
## Routes
### GET /rules
Returns a JSON array containing the server's rules
and keywords relating to each rule.
Example response:
>>> [
... ["Eat candy.", ["candy", "sweets"]],
... ["Wake up at 4 AM.", ["wake_up", "early", "early_bird"]],
... ["Take your medicine.", ["medicine", "health"]]
... ]
Since some of the the rules require links, this view
gives you the option to return rules in either Markdown
or HTML format by specifying the `link_format` query parameter
as either `md` or `html`. Specifying a different value than
`md` or `html` will return 400.
## Authentication
Does not require any authentication nor permissions.
"""
authentication_classes = ()
permission_classes = ()
@staticmethod
def _format_link(description: str, link: str, target: str) -> str:
"""
Build the markup for rendering the link.
This will render `link` with `description` as its description in the given
`target` language.
Arguments:
description (str):
A textual description of the string. Represents the content
between the `` tags in HTML, or the content between the
array brackets in Markdown.
link (str):
The resulting link that a user should be redirected to
upon clicking the generated element.
target (str):
One of `{'md', 'html'}`, denoting the target format that the
link should be rendered in.
Returns:
str:
The link, rendered appropriately for the given `target` format
using `description` as its textual description.
Raises:
ValueError:
If `target` is not `'md'` or `'html'`.
"""
if target == 'html':
return f'{description}'
elif target == 'md':
return f'[{description}]({link})'
else:
raise ValueError(
f"Can only template links to `html` or `md`, got `{target}`"
)
# `format` here is the result format, we have a link format here instead.
def get(self, request, format=None): # noqa: D102,ANN001,ANN201
"""
Returns a list of our community rules coupled with their keywords.
Each item in the returned list is a tuple with the rule as first item
and a list of keywords that match that rules as second item.
"""
link_format = request.query_params.get('link_format', 'md')
if link_format not in ('html', 'md'):
raise ParseError(
f"`format` must be `html` or `md`, got `{format}`."
)
discord_community_guidelines = self._format_link(
'Discord Community Guidelines',
'https://discordapp.com/guidelines',
link_format
)
discord_tos = self._format_link(
'Terms Of Service',
'https://discordapp.com/terms',
link_format
)
pydis_coc = self._format_link(
'Python Discord Code of Conduct',
'https://pythondiscord.com/pages/code-of-conduct/',
link_format
)
return Response([
(
f"Follow the {pydis_coc}.",
["coc", "conduct", "code"]
),
(
f"Follow the {discord_community_guidelines} and {discord_tos}.",
["discord", "guidelines", "discord_tos"]
),
(
"Respect staff members and listen to their instructions.",
["respect", "staff", "instructions"]
),
(
"Use English to the best of your ability. "
"Be polite if someone speaks English imperfectly.",
["english", "language"]
),
(
"Do not provide or request help on projects that may break laws, "
"breach terms of services, or are malicious or inappropriate.",
["infraction", "tos", "breach", "malicious", "inappropriate"]
),
(
"Do not post unapproved advertising.",
["ad", "ads", "advert", "advertising"]
),
(
"Keep discussions relevant to the channel topic. "
"Each channel's description tells you the topic.",
["off-topic", "topic", "relevance"]
),
(
"Do not help with ongoing exams. When helping with homework, "
"help people learn how to do the assignment without doing it for them.",
["exam", "exams", "assignment", "assignments", "homework"]
),
(
"Do not offer or ask for paid work of any kind.",
["paid", "work", "money"]
),
(
"Do not copy and paste answers from ChatGPT or similar AI tools.",
["gpt", "chatgpt", "gpt3", "ai"]
),
])
class GitHubArtifactsView(APIView):
"""
Provides utilities for interacting with the GitHub API and obtaining action artifacts.
## Routes
### GET /github/artifacts
Returns a download URL for the artifact requested.
{
'url': 'https://pipelines.actions.githubusercontent.com/...'
}
### Exceptions
In case of an error, the following body will be returned:
{
"error_type": "",
"error": "",
"requested_resource": "///"
}
## Authentication
Does not require any authentication nor permissions.
"""
authentication_classes = ()
permission_classes = ()
def get(
self,
request: Request,
*,
owner: str,
repo: str,
sha: str,
action_name: str,
artifact_name: str
) -> Response:
"""Return a download URL for the requested artifact."""
try:
url = github_utils.get_artifact(owner, repo, sha, action_name, artifact_name)
return Response({"url": url})
except github_utils.ArtifactProcessingError as e:
return Response({
"error_type": e.__class__.__name__,
"error": str(e),
"requested_resource": f"{owner}/{repo}/{sha}/{action_name}/{artifact_name}"
}, status=e.status)