Skip to content

Commit

Permalink
highlight themes
Browse files Browse the repository at this point in the history
medal percentage
problem filters
fixed gzip static files
less rating influence for partial solutions
save proxies to common volume
geeksforgeeks rating
codeforces tourist rating
deferring proxy
  • Loading branch information
aropan committed Sep 14, 2024
1 parent d8b5b27 commit cb201e4
Show file tree
Hide file tree
Showing 141 changed files with 5,206 additions and 599 deletions.
2 changes: 1 addition & 1 deletion config/cron
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DJANGO_ENV_FILE=.env.prod
*/1 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_CREATING_NOTIFICATIONS /usr/src/clist/run-manage.bash notification_to_task
*/1 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_SENDING_NOTIFICATIONS /usr/src/clist/run-manage.bash sendout_tasks
*/1 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_PARSING_STATISTICS /usr/src/clist/run-manage.bash parse_statistic
*/5 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_PARSING_ACCOUNTS /usr/src/clist/run-manage.bash parse_accounts_infos
*/3 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_PARSING_ACCOUNTS /usr/src/clist/run-manage.bash parse_accounts_infos
30 * * * * /usr/src/clist/run-manage.bash parse_archive_problems
*/1 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_CHECKING_LOGS /usr/src/clist/run-manage.bash check_logs
*/15 * * * * env MONITOR_NAME=SENTRY_CRON_MONITOR_SET_ACCOUNT_RANK /usr/src/clist/run-manage.bash set_account_rank
Expand Down
4 changes: 4 additions & 0 deletions legacy/module/acm.bsuir.by/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

$url = 'https://acm.bsuir.by/solve/api/v0/contests';
$data = curlexec($url, NULL, array('json_output' => true));
if (!is_array($data) || !is_array($data['contests'])) {
trigger_error("Expected array ['contests']", E_USER_WARNING);
return;
}
foreach ($data['contests'] as $contest) {
if (!preg_match('#bsuir#i', $contest['title'])) {
continue;
Expand Down
3 changes: 2 additions & 1 deletion legacy/module/codeany.org/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
$url = url_merge($url, "?format=json&page=$n_page");
$data = curlexec($url, NULL, array('json_output' => true));

if (!is_array($data['list_competitions'])) {
if (!is_array($data) || !is_array($data['list_competitions'])) {
trigger_error('Unexpected response', E_USER_WARNING);
break;
}

Expand Down
4 changes: 4 additions & 0 deletions legacy/module/codedrills.io/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
$python_bin = getenv('PYTHON_BIN');
exec($python_bin . " " . dirname(__FILE__) . "/decode.py $path $path");
$data = json_decode(file_get_contents($path), true);
if (!is_array($data) || !is_array($data[1])) {
trigger_error("Expected array [1]", E_USER_WARNING);
return;
}

foreach ($data[1] as $c) {
$c = $c[1];
Expand Down
5 changes: 5 additions & 0 deletions legacy/module/cups.online/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
$url = $URL;
$data = curlexec($url, null, array("json_output" => 1));

if (!is_array($data) || !isset($data['results'])) {
trigger_error('Unexpected response', E_USER_WARNING);
return;
}

foreach ($data['results'] as $contest) {
$categories = implode(' ', $contest['categories']);
foreach ($contest['round_set'] as $round) {
Expand Down
1 change: 1 addition & 0 deletions legacy/module/karelia.snarknews.info/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ function ($match) {
$title = $data['title'];
$date = $data['date'];
$url = $data['url'];
$title = preg_replace("/^day {$day}[.:]*\s*/i", '', $title);
$prefix = 'Day ' . $day . ': ';
if (substr($title, 0, strlen($prefix)) != $prefix) {
$title = $prefix . $title;
Expand Down
6 changes: 5 additions & 1 deletion legacy/module/luogu.com.cn/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
$data = curlexec($url, null, ['json_output' => true]);
$contests_data = pop_item($data, ['currentData', 'contests', 'result']);
$contests_info = pop_item($data, ['currentData', 'contests']);
$total_pages = $contests_info['count'] / $contests_info['perPage'];
if (!$contests_data || !$contests_info) {
trigger_error('Failed to parse contests data = ' . json_encode($data), E_USER_WARNING);
break;
}
$total_pages = $contests_info['perPage']? $contests_info['count'] / $contests_info['perPage'] : 0;

foreach ($contests_data as $c) {
$key = array_pop_assoc($c, 'id');
Expand Down
4 changes: 4 additions & 0 deletions legacy/module/supecoder.dev/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
for ($page = 1; $page == 1 || isset($data['totalPages']) && $page <= $data['totalPages']; ++$page) {
$url = "$URL/api/contests?page=$page&limit=10&sortBy=startTime&sortOrder=desc";
$data = curlexec($url, null, ['json_output' => true]);
if (!is_array($data) || !is_array($data['data'])) {
trigger_error("Unexpected data", E_USER_WARNING);
break;
}
foreach ($data['data'] as $c) {
$url = url_merge($HOST_URL, "/contest/{$c['slug']}?contestId={$c['_id']}");
$contests[] = array(
Expand Down
16 changes: 16 additions & 0 deletions src/clist/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,10 @@ def load_problem_rating_predictor(self):
model.load_model(model_path)
return model

@property
def problems_fields_types(self):
return self.problems_fields.get('types', {})


class BaseContestManager(BaseManager):
pass
Expand Down Expand Up @@ -756,6 +760,18 @@ def standings_per_page(self):
per_page = self.n_statistics
return per_page

@property
def full_problems_list(self):
problems = self.info.get('problems')
if not problems:
return
if isinstance(problems, dict):
division_problems = list(problems.get('division', {}).values())
problems = []
for a in division_problems:
problems.extend(a)
return problems

@property
def problems_list(self):
problems = self.info.get('problems')
Expand Down
166 changes: 120 additions & 46 deletions src/clist/templatetags/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
from django.contrib.contenttypes.models import ContentType
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.db.models import Value
from django.http import HttpResponseForbidden
from django.shortcuts import redirect
from django.template.base import Node
from django.template.defaultfilters import floatformat, slugify, stringfilter
from django.urls import NoReverseMatch, reverse
from django.utils.functional import keep_lazy
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django_countries.fields import countries
Expand All @@ -36,7 +39,7 @@

@register.filter
@stringfilter
def split(string, sep):
def split(string, sep=" "):
return string.split(sep)


Expand All @@ -63,11 +66,16 @@ def get_item(data, key, default=None):
if isinstance(key, str):
for sep in ('.', '__'):
if sep in key:
for k in key.split(sep):
data = get_item(data, k)
if data is None:
return default
return data
key = key.split(sep)
break

if isinstance(key, (list, tuple)):
for k in key:
data = get_item(data, k)
if data is None:
return default
return data

return default


Expand Down Expand Up @@ -491,6 +499,17 @@ def url_transform(request, *args, **kwargs):
return request.path + '?' + query


def allowed_redirect(url):
if not url_has_allowed_host_and_scheme(url, allowed_hosts=settings.ALLOWED_REDIRECT_HOSTS_):
return HttpResponseForbidden('Invalid URL')
return redirect(url)


def redirect_login(request):
next_url = quote_url(request.get_full_path())
return allowed_redirect(reverse('auth:login') + f'?next={next_url}')


@register.simple_tag
def query_fields(request, *args, before='&'):
updated = request.GET.copy()
Expand Down Expand Up @@ -574,6 +593,58 @@ def coder_color_class(resource, *values):
return f'coder-color coder-{rating["color"]}'


def circle_div(size, color, radius, width, fill, div_class=None, title=None):
if isinstance(fill, (float, int)):
v = fill * (size - 2 * width) + width
fill = f'''
<path
clip-path="url(#rating-clip-{size})"
d="M 0 {size} v-{round(v, 3)} h {size} 0 v{size} z"
style="fill: {color}"
/>
'''

div_size = radius * 2 + width
div_attrs = {}
if div_class:
div_attrs['class'] = div_class
if title:
div_attrs['title'] = title
div_attrs['data-html'] = 'true'
div_attrs['data-toggle'] = 'tooltip'
div_attrs = ' '.join(f'{k}="{v}"' for k, v in div_attrs.items())
return mark_safe(
f'''
<div
{div_attrs}
style="display: inline-block; vertical-align: top; padding-top: 1px"
>
<div style="height: {div_size}px; ">
<svg viewBox="-{width / 2} -{width / 2} {div_size} {div_size}" width="{div_size}" height="{div_size}">
<circle
style="stroke: {color}; fill: none; stroke-width: {width}px;"
cx="{radius}"
cy="{radius}"
r="{radius - 1}"
/>
{fill}
</svg>
</div>
</div>
''')


@register.simple_tag
def medal_percentage(medal, percent, info=None, size=16):
radius = size // 2
width = size // 10
title = f'{medal.title()}'
if info:
title += f'<br/>{info}'
title += f'<br/>{percent * 100:.2f}%'
return circle_div(size, 'inherit', radius, width, percent, title=title, div_class=f'{medal}-medal-percentage')


@register.simple_tag
def coder_color_circle(resource, *values, size=16, **kwargs):
Account = apps.get_model('ranking', 'Account')
Expand All @@ -585,26 +656,20 @@ def coder_color_circle(resource, *values, size=16, **kwargs):
radius = size // 2
width = size // 10
reverse_percent = resource.info.get('ratings', {}).get('reverse_circle_percent')
if ('prev' if reverse_percent else 'next') not in rating:
fill = f'<circle cx="{radius}" cy="{radius}" r="{size // 5}" style="fill: {color}"></circle>'
title = f'{value}'
else:

title = f'{value}'
has_percent = ('prev' if reverse_percent else 'next') in rating
if has_percent:
prv = max(rating.get('prev', rating['low']), 0)
nxt = rating.get('next', value)
percent = (value - prv) / (nxt - prv)
if reverse_percent:
percent = 1 - percent
v = percent * (size - 2 * width) + width
fill = f'''
<path
clip-path="url(#rating-clip-{size})"
d="M 0 {size} v-{round(v, 3)} h {size} 0 v{size} z"
style="fill: {color}"
/>
'''
title = f'{value}'
fill = percent
if 'next' in rating:
title += f' ({percent * 100:.1f}%)'
if not has_percent or rating.get('target'):
fill = f'<circle cx="{radius}" cy="{radius}" r="{size // 5}" style="fill: {color}"></circle>'
if 'name' in rating:
title += f'<br/>{rating["name"]}'
if len(values) == 1 and isinstance(values[0], Account):
Expand All @@ -614,30 +679,7 @@ def coder_color_circle(resource, *values, size=16, **kwargs):
percent = resource_rank * 100 / total_rank
percent = format_to_significant_digits(percent, 2)
title += f'<br/>#{resource_rank} of {total_rank} ({percent}%)'

div_size = radius * 2 + width
return mark_safe(
f'''
<div
class="coder-circle"
title="{title}"
data-toggle="tooltip"
data-html="true"
style="display: inline-block; vertical-align: top; padding-top: 1px"
>
<div style="height: {div_size}px; ">
<svg viewBox="-{width / 2} -{width / 2} {div_size} {div_size}" width="{div_size}" height="{div_size}">
<circle
style="stroke: {color}; fill: none; stroke-width: {width}px;"
cx="{radius}"
cy="{radius}"
r="{radius - 1}"
/>
{fill}
</svg>
</div>
</div>
''')
return circle_div(size, color, radius, width, fill, title=title, div_class='coder-circle')


@register.filter
Expand Down Expand Up @@ -945,9 +987,13 @@ def timestamp_to_datetime(value):

@register.filter
def highlight_class(lang):
if lang == 'python3':
return 'python'
return lang.replace(' ', '').lower()
lang = lang.lower()
lang = re.sub(r'\s*\(.*\)$', '', lang)
lang = re.sub(r'\s*[.0-9]+', '', lang)
lang = re.sub(r'([a-z])\+\+', r'\1pp', lang)
lang = re.sub(r'([a-z])\#', r'\1sharp', lang)
lang = re.sub(' ', '', lang)
return lang


@register.tag
Expand Down Expand Up @@ -1149,6 +1195,24 @@ def get_country_from_coder(context, coder):
return get_country_from(context, coder.country, coder.settings.get('custom_countries'))


@register.simple_tag
def get_country_from_members(accounts, members):
counter = defaultdict(int)
for member in members:
if 'account' not in member or member.get('without_country'):
continue
account = accounts.get(member['account'])
if account is None or not account.country:
continue
counter[account.country.code] += 1
if not counter:
return
country = max(counter, key=counter.get)
if counter[country] <= len(members) / 2:
return
return country


@register.simple_tag
def use_lightrope():
time = now()
Expand Down Expand Up @@ -1652,3 +1716,13 @@ def profile_url(account, resource=None, inner=None, html_class=None):
inner = inner or icon_to('profile')
html_class = f'class="{html_class}"' if html_class else ''
return mark_safe(f'<a href="{url}" {html_class} target="_blank" rel="noopener">{inner}</a>')


@register.simple_tag
def create_dict(**kwargs):
return kwargs


@register.simple_tag
def create_list(*args):
return list(args)
Loading

0 comments on commit cb201e4

Please sign in to comment.