Cloned DMOJ

This commit is contained in:
thanhluong 2020-01-21 15:35:58 +09:00
parent f623974b58
commit 49dc9ff10c
513 changed files with 132349 additions and 39 deletions

View file

View file

View file

@ -0,0 +1,17 @@
from django.core.management.base import BaseCommand
from judge.models import Judge
class Command(BaseCommand):
help = 'create a judge'
def add_arguments(self, parser):
parser.add_argument('name', help='the name of the judge')
parser.add_argument('auth_key', help='authentication key for the judge')
def handle(self, *args, **options):
judge = Judge()
judge.name = options['name']
judge.auth_key = options['auth_key']
judge.save()

View file

@ -0,0 +1,32 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from judge.models import Language, Profile
class Command(BaseCommand):
help = 'creates a user'
def add_arguments(self, parser):
parser.add_argument('name', help='username')
parser.add_argument('email', help='email, not necessary to be resolvable')
parser.add_argument('password', help='password for the user')
parser.add_argument('language', nargs='?', default=settings.DEFAULT_USER_LANGUAGE,
help='default language ID for user')
parser.add_argument('--superuser', action='store_true', default=False,
help="if specified, creates user with superuser privileges")
parser.add_argument('--staff', action='store_true', default=False,
help="if specified, creates user with staff privileges")
def handle(self, *args, **options):
usr = User(username=options['name'], email=options['email'], is_active=True)
usr.set_password(options['password'])
usr.is_superuser = options['superuser']
usr.is_staff = options['staff']
usr.save()
profile = Profile(user=usr)
profile.language = Language.objects.get(key=options['language'])
profile.save()

View file

@ -0,0 +1,16 @@
from django.core.management.base import BaseCommand, CommandError
from judge.utils.camo import client as camo_client
class Command(BaseCommand):
help = 'obtains the camo url for the specified url'
def add_arguments(self, parser):
parser.add_argument('url', help='url to use camo on')
def handle(self, *args, **options):
if camo_client is None:
raise CommandError('Camo not available')
print(camo_client.image_url(options['url']))

View file

@ -0,0 +1,27 @@
from django.core.management.base import BaseCommand, CommandError
from judge.models import Language, LanguageLimit
class Command(BaseCommand):
help = 'allows the problems that allow <source> to be submitted in <target>'
def add_arguments(self, parser):
parser.add_argument('source', help='language to copy from')
parser.add_argument('target', help='language to copy to')
def handle(self, *args, **options):
try:
source = Language.objects.get(key=options['source'])
except Language.DoesNotExist:
raise CommandError('Invalid source language: %s' % options['source'])
try:
target = Language.objects.get(key=options['target'])
except Language.DoesNotExist:
raise CommandError('Invalid target language: %s' % options['target'])
target.problem_set.set(source.problem_set.all())
LanguageLimit.objects.bulk_create(LanguageLimit(problem=ll.problem, language=target, time_limit=ll.time_limit,
memory_limit=ll.memory_limit)
for ll in LanguageLimit.objects.filter(language=source))

View file

@ -0,0 +1,23 @@
from django.core.management.base import BaseCommand
from judge.models import Problem, ProblemGroup, ProblemType
class Command(BaseCommand):
help = 'create an empty problem'
def add_arguments(self, parser):
parser.add_argument('code', help='problem code')
parser.add_argument('name', help='problem title')
parser.add_argument('body', help='problem description')
parser.add_argument('type', help='problem type')
parser.add_argument('group', help='problem group')
def handle(self, *args, **options):
problem = Problem()
problem.code = options['code']
problem.name = options['name']
problem.description = options['body']
problem.group = ProblemGroup.objects.get(name=options['group'])
problem.types = [ProblemType.objects.get(name=options['type'])]
problem.save()

View file

@ -0,0 +1,130 @@
import glob
import io
import os
import sys
from django.conf import settings
from django.core.management import CommandError
from django.core.management.commands.makemessages import Command as MakeMessagesCommand, check_programs
from judge.models import NavigationBar, ProblemType
class Command(MakeMessagesCommand):
def add_arguments(self, parser):
parser.add_argument('--locale', '-l', default=[], dest='locale', action='append',
help='Creates or updates the message files for the given locale(s) (e.g. pt_BR). '
'Can be used multiple times.')
parser.add_argument('--exclude', '-x', default=[], dest='exclude', action='append',
help='Locales to exclude. Default is none. Can be used multiple times.')
parser.add_argument('--all', '-a', action='store_true', dest='all',
default=False, help='Updates the message files for all existing locales.')
parser.add_argument('--no-wrap', action='store_true', dest='no_wrap',
default=False, help="Don't break long message lines into several lines.")
parser.add_argument('--no-obsolete', action='store_true', dest='no_obsolete',
default=False, help="Remove obsolete message strings.")
parser.add_argument('--keep-pot', action='store_true', dest='keep_pot',
default=False, help="Keep .pot file after making messages. Useful when debugging.")
def handle(self, *args, **options):
locale = options.get('locale')
exclude = options.get('exclude')
self.domain = 'dmoj-user'
self.verbosity = options.get('verbosity')
process_all = options.get('all')
# Need to ensure that the i18n framework is enabled
if settings.configured:
settings.USE_I18N = True
else:
settings.configure(USE_I18N=True)
# Avoid messing with mutable class variables
if options.get('no_wrap'):
self.msgmerge_options = self.msgmerge_options[:] + ['--no-wrap']
self.msguniq_options = self.msguniq_options[:] + ['--no-wrap']
self.msgattrib_options = self.msgattrib_options[:] + ['--no-wrap']
self.xgettext_options = self.xgettext_options[:] + ['--no-wrap']
if options.get('no_location'):
self.msgmerge_options = self.msgmerge_options[:] + ['--no-location']
self.msguniq_options = self.msguniq_options[:] + ['--no-location']
self.msgattrib_options = self.msgattrib_options[:] + ['--no-location']
self.xgettext_options = self.xgettext_options[:] + ['--no-location']
self.no_obsolete = options.get('no_obsolete')
self.keep_pot = options.get('keep_pot')
if locale is None and not exclude and not process_all:
raise CommandError("Type '%s help %s' for usage information." % (
os.path.basename(sys.argv[0]), sys.argv[1]))
self.invoked_for_django = False
self.locale_paths = []
self.default_locale_path = None
if os.path.isdir(os.path.join('conf', 'locale')):
self.locale_paths = [os.path.abspath(os.path.join('conf', 'locale'))]
self.default_locale_path = self.locale_paths[0]
self.invoked_for_django = True
else:
self.locale_paths.extend(settings.LOCALE_PATHS)
# Allow to run makemessages inside an app dir
if os.path.isdir('locale'):
self.locale_paths.append(os.path.abspath('locale'))
if self.locale_paths:
self.default_locale_path = self.locale_paths[0]
if not os.path.exists(self.default_locale_path):
os.makedirs(self.default_locale_path)
# Build locale list
locale_dirs = list(filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path)))
all_locales = list(map(os.path.basename, locale_dirs))
# Account for excluded locales
if process_all:
locales = all_locales
else:
locales = locale or all_locales
locales = set(locales) - set(exclude)
if locales:
check_programs('msguniq', 'msgmerge', 'msgattrib')
check_programs('xgettext')
try:
potfiles = self.build_potfiles()
# Build po files for each selected locale
for locale in locales:
if self.verbosity > 0:
self.stdout.write("processing locale %s\n" % locale)
for potfile in potfiles:
self.write_po_file(potfile, locale)
finally:
if not self.keep_pot:
self.remove_potfiles()
def find_files(self, root):
return []
def _emit_message(self, potfile, string):
potfile.write('''
msgid "%s"
msgstr ""
''' % string.replace('\\', r'\\').replace('\t', '\\t').replace('\n', '\\n').replace('"', '\\"'))
def process_files(self, file_list):
with io.open(os.path.join(self.default_locale_path, 'dmoj-user.pot'), 'w', encoding='utf-8') as potfile:
if self.verbosity > 1:
self.stdout.write('processing navigation bar')
for label in NavigationBar.objects.values_list('label', flat=True):
if self.verbosity > 2:
self.stdout.write('processing navigation item label "%s"\n' % label)
self._emit_message(potfile, label)
if self.verbosity > 1:
self.stdout.write('processing problem types')
for name in ProblemType.objects.values_list('full_name', flat=True):
if self.verbosity > 2:
self.stdout.write('processing problem type name "%s"\n' % name)
self._emit_message(potfile, name)

View file

@ -0,0 +1,58 @@
import os
import shutil
import sys
from django.conf import settings
from django.core.management.base import BaseCommand
from django.template.loader import get_template
from django.utils import translation
from judge.models import Problem, ProblemTranslation
from judge.pdf_problems import DefaultPdfMaker, PhantomJSPdfMaker, PuppeteerPDFRender, SlimerJSPdfMaker
class Command(BaseCommand):
help = 'renders a PDF file of a problem'
def add_arguments(self, parser):
parser.add_argument('code', help='code of problem to render')
parser.add_argument('directory', nargs='?', help='directory to store temporaries')
parser.add_argument('-l', '--language', default=settings.LANGUAGE_CODE,
help='language to render PDF in')
parser.add_argument('-p', '--phantomjs', action='store_const', const=PhantomJSPdfMaker,
default=DefaultPdfMaker, dest='engine')
parser.add_argument('-s', '--slimerjs', action='store_const', const=SlimerJSPdfMaker, dest='engine')
parser.add_argument('-c', '--chrome', '--puppeteer', action='store_const',
const=PuppeteerPDFRender, dest='engine')
def handle(self, *args, **options):
try:
problem = Problem.objects.get(code=options['code'])
except Problem.DoesNotExist:
print('Bad problem code')
return
try:
trans = problem.translations.get(language=options['language'])
except ProblemTranslation.DoesNotExist:
trans = None
directory = options['directory']
with options['engine'](directory, clean_up=directory is None) as maker, \
translation.override(options['language']):
problem_name = problem.name if trans is None else trans.name
maker.html = get_template('problem/raw.html').render({
'problem': problem,
'problem_name': problem_name,
'description': problem.description if trans is None else trans.description,
'url': '',
'math_engine': maker.math_engine,
}).replace('"//', '"https://').replace("'//", "'https://")
maker.title = problem_name
for file in ('style.css', 'pygment-github.css', 'mathjax_config.js'):
maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file))
maker.make(debug=True)
if not maker.success:
print(maker.log, file=sys.stderr)
elif directory is None:
shutil.move(maker.pdffile, problem.code + '.pdf')

View file

@ -0,0 +1,33 @@
import threading
from django.conf import settings
from django.core.management.base import BaseCommand
from judge.bridge import DjangoHandler, DjangoServer
from judge.bridge import DjangoJudgeHandler, JudgeServer
class Command(BaseCommand):
def handle(self, *args, **options):
judge_handler = DjangoJudgeHandler
try:
import netaddr # noqa: F401, imported to see if it exists
except ImportError:
pass
else:
proxies = settings.BRIDGED_JUDGE_PROXIES
if proxies:
judge_handler = judge_handler.with_proxy_set(proxies)
judge_server = JudgeServer(settings.BRIDGED_JUDGE_ADDRESS, judge_handler)
django_server = DjangoServer(judge_server.judges, settings.BRIDGED_DJANGO_ADDRESS, DjangoHandler)
# TODO: Merge the two servers
threading.Thread(target=django_server.serve_forever).start()
try:
judge_server.serve_forever()
except KeyboardInterrupt:
pass
finally:
django_server.stop()

View file

@ -0,0 +1,53 @@
from django.conf import settings
from django.core.management.base import BaseCommand
from moss import *
from judge.models import Contest, ContestParticipation, Submission
class Command(BaseCommand):
help = 'Checks for duplicate code using MOSS'
LANG_MAPPING = {
('C++', MOSS_LANG_CC),
('C', MOSS_LANG_C),
('Java', MOSS_LANG_JAVA),
('Python', MOSS_LANG_PYTHON),
}
def add_arguments(self, parser):
parser.add_argument('contest', help='the id of the contest')
def handle(self, *args, **options):
moss_api_key = settings.MOSS_API_KEY
if moss_api_key is None:
print('No MOSS API Key supplied')
return
contest = options['contest']
for problem in Contest.objects.get(key=contest).problems.order_by('code'):
print('========== %s / %s ==========' % (problem.code, problem.name))
for dmoj_lang, moss_lang in self.LANG_MAPPING:
print("%s: " % dmoj_lang, end=' ')
subs = Submission.objects.filter(
contest__participation__virtual__in=(ContestParticipation.LIVE, ContestParticipation.SPECTATE),
contest__participation__contest__key=contest,
result='AC', problem__id=problem.id,
language__common_name=dmoj_lang,
).values_list('user__user__username', 'source__source')
if not subs:
print('<no submissions>')
continue
moss_call = MOSS(moss_api_key, language=moss_lang, matching_file_limit=100,
comment='%s - %s' % (contest, problem.code))
users = set()
for username, source in subs:
if username in users:
continue
users.add(username)
moss_call.add_file_from_memory(username, source.encode('utf-8'))
print('(%d): %s' % (subs.count(), moss_call.process()))