Skip to content

Commit

Permalink
pg_repack fix
Browse files Browse the repository at this point in the history
detect major contests fix
standings upload solution
pretty row for coders and account pages
field_to_input
  • Loading branch information
aropan committed Dec 16, 2024
1 parent e0d9a56 commit fa42e17
Show file tree
Hide file tree
Showing 52 changed files with 564 additions and 169 deletions.
10 changes: 4 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ RUN wget https://github.com/stunnel/static-curl/releases/download/8.6.0-1/curl-l
rm /tmp/curl.tar.xz

# psql
RUN apt install -y postgresql-client

RUN apt update --fix-missing
RUN apt update --fix-missing && apt install -y postgresql-client

ENV APPDIR=/usr/src/clist
WORKDIR $APPDIR
Expand Down Expand Up @@ -115,9 +113,9 @@ RUN apk add --no-cache --virtual .build-deps \
zlib-dev \
bash \
util-linux \
gawk \
&& cd /tmp \
&& git clone https://github.com/reorg/pg_repack.git \
gawk
RUN cd /tmp \
&& git clone --depth 1 --branch ver_1.5.1 https://github.com/reorg/pg_repack.git \
&& cd pg_repack \
&& make \
&& make install \
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ services:
- shared_files:/usr/src/clist/sharedfiles/
- ./logs/production:/usr/src/clist/logs/
- ./legacy/logs/:/usr/src/clist/logs/legacy/
- ./logs/postgres/:/usr/src/clist/logs/postgres/
depends_on:
- db
- memcached
Expand Down
2 changes: 1 addition & 1 deletion legacy/module/codecracker.arhn.in/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

$data = curlexec($URL, null, array("http_header" => array('content-type: application/json'), "json_output" => 1));

if (!is_array($data)) {
if (!is_array($data) || isset($data['error'])) {
trigger_error('data = ' . json_encode($data), E_USER_WARNING);
return;
}
Expand Down
2 changes: 1 addition & 1 deletion legacy/module/nerc.itmo.ru_school/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function replace_months($page, $with_year) {
$title = htmlspecialchars_decode($title);
$title = preg_replace('#\s+#', ' ', trim($title));

$key = "{$season}_vkoshp";
$key = "{$season}-vkoshp";

$_contests[$key] = array(
'start_time' => $date,
Expand Down
4 changes: 2 additions & 2 deletions legacy/update.php
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ function($m) {
$db->query("DELETE FROM clist_contest WHERE $delete_update", true);
}

$contest['was_auto_added'] = 1;
$contest['is_auto_added'] = 1;
$contest['auto_updated'] = date("Y-m-d H:i:s");

foreach ($contest as $field => $value)
Expand Down Expand Up @@ -423,7 +423,7 @@ function($m) {
}
$resources_filter .= "(resource_id = $resource_id AND $time_filter)";
}
$query = "was_auto_added = true AND ($resources_filter)";
$query = "is_auto_added = true AND ($resources_filter)";
$to_be_removed = $db->select("clist_contest", "*", $query);
if ($to_be_removed) {
$filename_log = LOGREMOVEDDIR . date("Y-m-d_H-i-s", time()) . '.txt';
Expand Down
5 changes: 3 additions & 2 deletions src/clist/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def parse_statistic(self, request, queryset):
'standings_kind', 'registration_url', 'trial_standings_url']}],
['Date information', {'fields': ['start_time', 'end_time', 'duration_in_secs']}],
['Secury information', {'fields': ['key']}],
['Addition information', {'fields': ['was_auto_added', 'auto_updated', 'n_statistics', 'has_hidden_results',
['Addition information', {'fields': ['is_auto_added', 'auto_updated', 'n_statistics', 'has_hidden_results',
'writers', 'calculate_time', 'info', 'variables', 'invisible',
'is_rated', 'is_promoted', 'with_medals',
'related', 'merging_contests', 'series',
Expand All @@ -69,7 +69,8 @@ def parse_statistic(self, request, queryset):
'rating_prediction_timing', 'created', 'modified', 'updated']}],
['Rating', {'fields': ['rating_prediction_hash', 'has_fixed_rating_prediction_field',
'rating_prediction_fields']}],
['Problem', {'fields': ['n_problems', 'problem_rating_hash', 'problem_rating_update_required']}],
['Problem', {'fields': ['n_problems', 'problem_rating_hash', 'problem_rating_update_required',
'hide_unsolved_standings_problems', 'upload_solved_problems_solutions']}],
['Submission', {'fields': ['has_submissions', 'has_submissions_tests']}],
]
list_display = ['title', 'host', 'start_time', 'url', 'is_rated', 'invisible', 'key', 'standings_url',
Expand Down
3 changes: 2 additions & 1 deletion src/clist/api/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re

import arrow
from django.conf import settings
from django.core.exceptions import FieldDoesNotExist
from django.db.models import CharField, IntegerField, JSONField, Value
from django.db.models.constants import LOOKUP_SEP
Expand Down Expand Up @@ -352,7 +353,7 @@ def dehydrate(self, *args, **kwargs):
for k in list(problem.keys()):
if k.startswith('_'):
problem.pop(k, None)
for k in 'solution', 'external_solution':
for k in settings.PROBLEM_API_IGNORE_FIELD:
problem.pop(k, None)

more_fields = bundle.data['more_fields']
Expand Down
35 changes: 35 additions & 0 deletions src/clist/migrations/0166_contest_promotion.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions src/clist/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ class Contest(BaseModel):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True, db_index=True)
was_auto_added = models.BooleanField(default=False)
is_auto_added = models.BooleanField(default=False)
auto_updated = models.DateTimeField(auto_now_add=True)

event_logs = GenericRelation('logify.EventLog', related_query_name='contest')
Expand All @@ -528,6 +528,9 @@ class Contest(BaseModel):

is_promoted = models.BooleanField(default=None, null=True, blank=True, db_index=True)

hide_unsolved_standings_problems = models.BooleanField(default=None, null=True, blank=True)
upload_solved_problems_solutions = models.BooleanField(default=None, null=True, blank=True)

objects = BaseContestManager()
visible = VisibleContestManager()
significant = SignificantContestManager()
Expand Down Expand Up @@ -1206,7 +1209,7 @@ class PromotionTimeAttribute(models.TextChoices):


class Promotion(BaseModel):
name = models.CharField(max_length=50)
name = models.CharField(max_length=200)
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
timer_message = models.CharField(max_length=200, null=True, blank=True)
time_attribute = models.CharField(max_length=50, choices=PromotionTimeAttribute.choices)
Expand Down
30 changes: 19 additions & 11 deletions src/clist/templatetags/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -1353,13 +1353,12 @@ def rating_from_probability(b, p, min_rating=0, max_rating=5000):


@register.simple_tag
def icon_to(value, default=None, icons=None, html_class=None, **kwargs):
def icon_to(value, default=None, icons=None, html_class=None, inner='', **kwargs):
icons = icons or settings.FONTAWESOME_ICONS_
if default is None:
default = value.title().replace('_', ' ')
if value in icons:
value = icons[value]
inner = ''
params = kwargs
if isinstance(value, dict):
params = deepcopy(value)
Expand All @@ -1374,15 +1373,14 @@ def icon_to(value, default=None, icons=None, html_class=None, **kwargs):
html_class = params['class']
if 'title' in params:
default = params['title']

if default:
inner += f' title="{default}" data-toggle="tooltip"'
if html_class:
inner += f' class="{html_class}"'
ret = f'<span{inner}>{value}</span>'
return mark_safe(ret)
else:
return default
value = title_field(value)
if default:
inner += f' title="{default}" data-toggle="tooltip"'
if html_class:
inner += f' class="{html_class}"'
ret = f'<span{inner}>{value}</span>'
return mark_safe(ret)


@register.simple_tag(takes_context=True)
Expand Down Expand Up @@ -1591,7 +1589,10 @@ def coder_account_filter(queryset, entity, row_number_field=None, operator=None)
return []
ret = queryset.filter(pk=entity.pk).annotate(delete_on_duplicate=Value(True))
if row_number_field:
value = getattr(entity, row_number_field)
if hasattr(entity, row_number_field):
value = getattr(entity, row_number_field)
else:
value = queryset.filter(pk=entity.pk).values_list(row_number_field, flat=True).first()
if value is not None:
row_number = queryset.filter(**{row_number_field + operator: value}).count() + 1
ret = ret.annotate(row_number=Value(row_number))
Expand All @@ -1615,6 +1616,13 @@ def is_yes(value):
return str(value).lower() in settings.YES_


@register.filter
def is_optional_yes(value):
if value is None:
return None
return str(value).lower() in settings.YES_


@register.filter
def get_admin_url(obj):
if obj is None:
Expand Down
9 changes: 6 additions & 3 deletions src/clist/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ def similar_contests_replacing(match):


def similar_contests_queryset(contest):
title = f'^{contest.title}$'
regex = get_similar_contests_regex()
title_regex = re.sub(regex, similar_contests_replacing, title)
contests_filter = Q(title__iregex=title_regex, resource_id=contest.resource_id, stage__isnull=True)
title_regex = re.sub(regex, similar_contests_replacing, f'^{contest.title}$')
contests_filter = Q(title__iregex=title_regex)
if not re.match('^[^a-zA-Z]*$', contest.key):
key_regex = re.sub(regex, similar_contests_replacing, f'^{contest.key}$')
contests_filter |= Q(key__iregex=key_regex)
contests_filter &= Q(resource_id=contest.resource_id, stage__isnull=True)
return contest._meta.model.objects.filter(contests_filter)
63 changes: 36 additions & 27 deletions src/clist/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import re
from collections import OrderedDict
from copy import deepcopy
Expand All @@ -9,6 +10,7 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.core.management.commands import dumpdata
from django.db import transaction
from django.db.models import Avg, Count, F, FloatField, IntegerField, Max, Min, OuterRef, Prefetch, Q, Subquery
Expand Down Expand Up @@ -189,33 +191,30 @@ def get_events(request):
elif status == 'running':
contests = contests.filter(start_time__lte=now, end_time__gte=now)

try:
result = []
for contest in contests.filter(query):
color = contest.resource.color
if past_action not in ['show', 'hide'] and contest.end_time < now:
color = contest.resource.info.get('get_events', {}).get('colors', {}).get(past_action, color)

start_time = (contest.start_time + timedelta(minutes=offset)).strftime("%Y-%m-%dT%H:%M:%S")
end_time = (contest.end_time + timedelta(minutes=offset)).strftime("%Y-%m-%dT%H:%M:%S")
c = {
'id': contest.pk,
'title': contest.title,
'host': contest.host,
'url': contest.actual_url,
'start': start_time,
'end': end_time,
'countdown': contest.next_time_to(now),
'hr_duration': contest.hr_duration,
'color': color,
'icon': contest.resource.icon,
'allDay': contest.full_duration >= timedelta(days=1),
}
if coder:
c['favorite'] = contest.is_favorite
result.append(c)
except Exception as e:
return JsonResponse({'error': str(e)}, safe=False, status=400)
result = []
for contest in contests.filter(query):
color = contest.resource.color
if past_action not in ['show', 'hide'] and contest.end_time < now:
color = contest.resource.info.get('get_events', {}).get('colors', {}).get(past_action, color)

start_time = (contest.start_time + timedelta(minutes=offset)).strftime("%Y-%m-%dT%H:%M:%S")
end_time = (contest.end_time + timedelta(minutes=offset)).strftime("%Y-%m-%dT%H:%M:%S")
c = {
'id': contest.pk,
'title': contest.title,
'host': contest.host,
'url': contest.actual_url,
'start': start_time,
'end': end_time,
'countdown': contest.next_time_to(now),
'hr_duration': contest.hr_duration,
'color': color,
'icon': contest.resource.icon,
'allDay': contest.full_duration >= timedelta(days=1),
}
if coder:
c['favorite'] = contest.is_favorite
result.append(c)
return JsonResponse(result, safe=False)


Expand Down Expand Up @@ -993,6 +992,16 @@ def update_problems(contest, problems=None, force=False):
break
old_problem_ids.remove(opt_old_problem.id)
opt_old_problem.contest = opt_new_problem.contest

# FIXME: 'GenericRelation' object has no attribute 'field'
for activity in opt_old_problem.activities.all():
try:
activity.object_id = opt_new_problem.id
activity.validate_unique()
except ValidationError as e:
logging.warning(f'ValidationError: {e}')
activity.delete()

MergedModelInstance.create(opt_new_problem, [opt_old_problem])
opt_old_problem.delete()

Expand Down
2 changes: 1 addition & 1 deletion src/my_oauth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ def services_dumpdata(request):

def form(request, uuid):
form = get_object_or_404(Form.objects, pk=uuid)
token_id = request.session.get('form_token_id', None)
token_id = request.session.pop('form_token_id', None)
token = Token.objects.filter(pk=token_id).first() if token_id else None

if form.is_closed():
Expand Down
6 changes: 6 additions & 0 deletions src/notification/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class NotificationAdmin(BaseModelAdmin):
@admin_register(Subscription)
class SubscriptionAdmin(BaseModelAdmin):
list_display = ['coder', 'method', 'enable', 'n_accounts', 'n_coders',
'top_n', '_with_first_accepted',
'resource', 'contest', 'coder_list', 'coder_chat']
list_filter = ['enable', 'method']
search_fields = ['coder__username', 'accounts__key', 'coders__username']
Expand All @@ -24,6 +25,11 @@ def n_accounts(self, obj):
def n_coders(self, obj):
return obj.n_coders

def _with_first_accepted(self, obj):
return bool(obj.with_first_accepted)
_with_first_accepted.boolean = True
_with_first_accepted.short_description = 'AC'

def get_queryset(self, request):
ret = super().get_queryset(request)
ret = ret.annotate(n_accounts=SubqueryCount('accounts'))
Expand Down
4 changes: 4 additions & 0 deletions src/notification/management/commands/sendout_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ def handle(self, *args, **options):
deleted = 0
for is_email_iteration in range(2):
for task in tqdm.tqdm(qs, 'sending'):
if task.notification is None:
if task.id:
task.delete()
continue
is_email = task.notification.method == settings.NOTIFICATION_CONF.EMAIL
if is_email_iteration != is_email:
continue
Expand Down
Loading

0 comments on commit fa42e17

Please sign in to comment.