NDOJ/judge/jinja2/reference.py

202 lines
5.5 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
import re
from collections import defaultdict
from urllib.parse import urljoin
from ansi2html import Ansi2HTMLConverter
from django.contrib.auth.models import AbstractUser
from django.urls import reverse
from django.utils.safestring import mark_safe
from lxml.html import Element
from judge import lxml_tree
from judge.models import Contest, Problem, Profile
from judge.ratings import rating_class, rating_progress
from . import registry
2022-05-14 17:57:27 +00:00
rereference = re.compile(r"\[(r?user):(\w+)\]")
2020-01-21 06:35:58 +00:00
def get_user(username, data):
if not data:
2022-05-14 17:57:27 +00:00
element = Element("span")
2020-01-21 06:35:58 +00:00
element.text = username
return element
2022-05-14 17:57:27 +00:00
element = Element("span", {"class": Profile.get_user_css_class(*data)})
link = Element("a", {"href": reverse("user_page", args=[username])})
2020-01-21 06:35:58 +00:00
link.text = username
element.append(link)
return element
def get_user_rating(username, data):
if not data:
2022-05-14 17:57:27 +00:00
element = Element("span")
2020-01-21 06:35:58 +00:00
element.text = username
return element
rating = data[1]
2022-05-14 17:57:27 +00:00
element = Element(
"a", {"class": "rate-group", "href": reverse("user_page", args=[username])}
)
2020-01-21 06:35:58 +00:00
if rating:
rating_css = rating_class(rating)
2022-05-14 17:57:27 +00:00
rate_box = Element("span", {"class": "rate-box " + rating_css})
rate_box.append(
Element("span", {"style": "height: %3.fem" % rating_progress(rating)})
)
user = Element("span", {"class": "rating " + rating_css})
2020-01-21 06:35:58 +00:00
user.text = username
element.append(rate_box)
element.append(user)
else:
element.text = username
return element
def get_user_info(usernames):
2022-05-14 17:57:27 +00:00
return {
name: (rank, rating)
for name, rank, rating in Profile.objects.filter(
user__username__in=usernames
).values_list("user__username", "display_rank", "rating")
}
2020-01-21 06:35:58 +00:00
2020-07-03 02:50:31 +00:00
def get_user_from_text(text):
user_list = set()
for i in rereference.finditer(text):
2022-05-14 17:57:27 +00:00
user_list.add(text[i.start() + 6 : i.end() - 1])
2020-07-03 02:50:31 +00:00
return Profile.objects.filter(user__username__in=user_list)
2020-01-21 06:35:58 +00:00
reference_map = {
2022-05-14 17:57:27 +00:00
"user": (get_user, get_user_info),
"ruser": (get_user_rating, get_user_info),
2020-01-21 06:35:58 +00:00
}
def process_reference(text):
# text/tail -> text/tail + elements
last = 0
tail = text
prev = None
elements = []
for piece in rereference.finditer(text):
if prev is None:
2022-05-14 17:57:27 +00:00
tail = text[last : piece.start()]
2020-01-21 06:35:58 +00:00
else:
2022-05-14 17:57:27 +00:00
prev.append(text[last : piece.start()])
2020-01-21 06:35:58 +00:00
prev = list(piece.groups())
elements.append(prev)
last = piece.end()
if prev is not None:
prev.append(text[last:])
return tail, elements
def populate_list(queries, list, element, tail, children):
if children:
for elem in children:
queries[elem[0]].append(elem[1])
list.append((element, tail, children))
def update_tree(list, results, is_tail=False):
for element, text, children in list:
after = []
for type, name, tail in children:
child = reference_map[type][0](name, results[type].get(name))
child.tail = tail
after.append(child)
after = iter(reversed(after))
if is_tail:
element.tail = text
link = element.getnext()
if link is None:
link = next(after)
element.getparent().append(link)
else:
element.text = text
link = next(after)
element.insert(0, link)
for child in after:
link.addprevious(child)
link = child
@registry.filter
def reference(text):
tree = lxml_tree.fromstring(text)
texts = []
tails = []
queries = defaultdict(list)
for element in tree.iter():
if element.text:
populate_list(queries, texts, element, *process_reference(element.text))
if element.tail:
populate_list(queries, tails, element, *process_reference(element.tail))
results = {type: reference_map[type][1](values) for type, values in queries.items()}
update_tree(texts, results, is_tail=False)
update_tree(tails, results, is_tail=True)
return tree
@registry.filter
def item_title(item):
if isinstance(item, Problem):
return item.name
elif isinstance(item, Contest):
return item.name
2022-05-14 17:57:27 +00:00
return "<Unknown>"
2020-01-21 06:35:58 +00:00
@registry.function
2022-05-14 17:57:27 +00:00
@registry.render_with("user/link.html")
2020-01-21 06:35:58 +00:00
def link_user(user):
if isinstance(user, Profile):
2023-10-11 00:37:36 +00:00
profile = user
2020-01-21 06:35:58 +00:00
elif isinstance(user, AbstractUser):
profile = user.profile
2022-05-14 17:57:27 +00:00
elif type(user).__name__ == "ContestRankingProfile":
2023-10-11 00:37:36 +00:00
profile = user
2020-01-21 06:35:58 +00:00
else:
2022-05-14 17:57:27 +00:00
raise ValueError("Expected profile or user, got %s" % (type(user),))
2023-10-11 00:37:36 +00:00
return {"profile": profile}
2020-01-21 06:35:58 +00:00
@registry.function
2022-05-14 17:57:27 +00:00
@registry.render_with("user/link-list.html")
2020-01-21 06:35:58 +00:00
def link_users(users):
2022-05-14 17:57:27 +00:00
return {"users": users}
2020-01-21 06:35:58 +00:00
@registry.function
2022-05-14 17:57:27 +00:00
@registry.render_with("runtime-version-fragment.html")
2020-01-21 06:35:58 +00:00
def runtime_versions(versions):
2022-05-14 17:57:27 +00:00
return {"runtime_versions": versions}
2020-01-21 06:35:58 +00:00
2022-05-14 17:57:27 +00:00
@registry.filter(name="absolutify")
2020-01-21 06:35:58 +00:00
def absolute_links(text, url):
tree = lxml_tree.fromstring(text)
2022-05-14 17:57:27 +00:00
for anchor in tree.xpath(".//a"):
href = anchor.get("href")
2020-01-21 06:35:58 +00:00
if href:
2022-05-14 17:57:27 +00:00
anchor.set("href", urljoin(url, href))
2020-01-21 06:35:58 +00:00
return tree
2022-05-14 17:57:27 +00:00
@registry.function(name="urljoin")
2020-01-21 06:35:58 +00:00
def join(first, second, *rest):
if not rest:
return urljoin(first, second)
return urljoin(urljoin(first, second), *rest)
2022-05-14 17:57:27 +00:00
@registry.filter(name="ansi2html")
2020-01-21 06:35:58 +00:00
def ansi2html(s):
return mark_safe(Ansi2HTMLConverter(inline=True).convert(s, full=False))