Cloned DMOJ
This commit is contained in:
parent
f623974b58
commit
49dc9ff10c
513 changed files with 132349 additions and 39 deletions
0
judge/management/__init__.py
Normal file
0
judge/management/__init__.py
Normal file
0
judge/management/commands/__init__.py
Normal file
0
judge/management/commands/__init__.py
Normal file
17
judge/management/commands/addjudge.py
Normal file
17
judge/management/commands/addjudge.py
Normal 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()
|
32
judge/management/commands/adduser.py
Normal file
32
judge/management/commands/adduser.py
Normal 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()
|
16
judge/management/commands/camo.py
Normal file
16
judge/management/commands/camo.py
Normal 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']))
|
27
judge/management/commands/copy_language.py
Normal file
27
judge/management/commands/copy_language.py
Normal 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))
|
23
judge/management/commands/create_problem.py
Normal file
23
judge/management/commands/create_problem.py
Normal 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()
|
130
judge/management/commands/makedmojmessages.py
Normal file
130
judge/management/commands/makedmojmessages.py
Normal 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)
|
58
judge/management/commands/render_pdf.py
Normal file
58
judge/management/commands/render_pdf.py
Normal 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')
|
33
judge/management/commands/runbridged.py
Normal file
33
judge/management/commands/runbridged.py
Normal 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()
|
53
judge/management/commands/runmoss.py
Normal file
53
judge/management/commands/runmoss.py
Normal 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()))
|
Loading…
Add table
Add a link
Reference in a new issue