Skip to content

Commit

Permalink
Simplify PDF rendering backend
Browse files Browse the repository at this point in the history
- Drop Puppeteer and Selenium backends
- "Inline" pdfoid, and move `pdf_problems.py` to `utils` + rename it
  `pdfoid.py`
- Drop a lot of pointless error checking; if we fail we'll want to raise
  a 500 regardless

Co-authored-by: Quantum <[email protected]>
  • Loading branch information
2 people authored and hieplpvip committed Mar 12, 2023
1 parent 34f3299 commit 5cf986b
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 386 deletions.
41 changes: 23 additions & 18 deletions dmoj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import datetime
import os
import tempfile

from django.utils.translation import gettext_lazy as _
from django_jinja.builtins import DEFAULT_EXTENSIONS
Expand Down Expand Up @@ -165,8 +164,6 @@

SITE_FULL_URL = None # ie 'https://oj.vnoi.info', please remove the last / if needed

NODEJS = '/usr/bin/node'
EXIFTOOL = '/usr/bin/exiftool'
ACE_URL = '//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3'
SELECT2_JS_URL = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
SELECT2_CSS_URL = '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css'
Expand All @@ -175,41 +172,54 @@
DMOJ_CAMO_KEY = None
DMOJ_CAMO_HTTPS = False
DMOJ_CAMO_EXCLUDE = ()

DMOJ_PROBLEM_DATA_ROOT = None

DMOJ_PROBLEM_MIN_TIME_LIMIT = 0 # seconds
DMOJ_PROBLEM_MAX_TIME_LIMIT = 60 # seconds
DMOJ_PROBLEM_MIN_MEMORY_LIMIT = 0 # kilobytes
DMOJ_PROBLEM_MAX_MEMORY_LIMIT = 1048576 # kilobytes
DMOJ_PROBLEM_MIN_PROBLEM_POINTS = 0
DMOJ_PROBLEM_HOT_PROBLEM_COUNT = 7

DMOJ_PROBLEM_STATEMENT_DISALLOWED_CHARACTERS = {'“', '”', '‘', '’', '−', 'ff', 'fi', 'fl', 'ffi', 'ffl'}
DMOJ_RATING_COLORS = True
DMOJ_EMAIL_THROTTLING = (10, 60)
VNOJ_DISCORD_WEBHOOK_THROTTLING = (10, 60) # Max 10 messages in 60 seconds
DMOJ_STATS_LANGUAGE_THRESHOLD = 10
DMOJ_SUBMISSIONS_REJUDGE_LIMIT = 10

# Maximum number of submissions a single user can queue without the `spam_submission` permission
DMOJ_SUBMISSION_LIMIT = 2
DMOJ_SUBMISSIONS_REJUDGE_LIMIT = 10

# Whether to allow users to view source code: 'all' | 'all-solved' | 'only-own'
DMOJ_SUBMISSION_SOURCE_VISIBILITY = 'all-solved'
DMOJ_BLOG_NEW_PROBLEM_COUNT = 7
DMOJ_TOTP_TOLERANCE_HALF_MINUTES = 1
DMOJ_SCRATCH_CODES_COUNT = 5
DMOJ_USER_MAX_ORGANIZATION_COUNT = 3

# Whether to allow users to download their data
DMOJ_USER_DATA_DOWNLOAD = False
DMOJ_USER_DATA_CACHE = ''
DMOJ_USER_DATA_INTERNAL = ''
DMOJ_USER_DATA_DOWNLOAD_RATELIMIT = datetime.timedelta(days=1)

# Whether to allow contest authors to download contest data
DMOJ_CONTEST_DATA_DOWNLOAD = False
DMOJ_CONTEST_DATA_CACHE = ''
DMOJ_CONTEST_DATA_INTERNAL = ''
DMOJ_CONTEST_DATA_DOWNLOAD_RATELIMIT = datetime.timedelta(days=1)

DMOJ_COMMENT_VOTE_HIDE_THRESHOLD = -5
DMOJ_COMMENT_REPLY_TIMEFRAME = datetime.timedelta(days=365)
DMOJ_PDF_PROBLEM_CACHE = ''
DMOJ_PDF_PROBLEM_TEMP_DIR = tempfile.gettempdir()

DMOJ_PDF_PDFOID_URL = None
# Optional but recommended to save resources, path on disk to cache PDFs
DMOJ_PDF_PROBLEM_CACHE = None
# Optional, URL serving DMOJ_PDF_PROBLEM_CACHE with X-Accel-Redirect
DMOJ_PDF_PROBLEM_INTERNAL = None

DMOJ_STATS_LANGUAGE_THRESHOLD = 10
DMOJ_STATS_SUBMISSION_RESULT_COLORS = {
'TLE': '#a3bcbd',
'AC': '#00a92a',
Expand Down Expand Up @@ -260,16 +270,6 @@
TERMS_OF_SERVICE_URL = None
DEFAULT_USER_LANGUAGE = 'CPP17'

PUPPETEER_MODULE = '/usr/lib/node_modules/puppeteer'
PUPPETEER_PAPER_SIZE = 'Letter'

USE_SELENIUM = False
SELENIUM_CUSTOM_CHROME_PATH = None
SELENIUM_CHROMEDRIVER_PATH = 'chromedriver'

USE_PDFOID = False
PDFOID_URL = ''

INLINE_JQUERY = True
INLINE_FONTAWESOME = True
JQUERY_JS = '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js'
Expand Down Expand Up @@ -725,6 +725,11 @@
except IOError:
pass

# Compute these values after local_settings.py is loaded
if DMOJ_PDF_PDFOID_URL:
# If a cache is configured, it must already exist and be a directory
assert DMOJ_PDF_PROBLEM_CACHE is None or os.path.isdir(DMOJ_PDF_PROBLEM_CACHE)
# If using X-Accel-Redirect, the cache directory must be configured
assert DMOJ_PDF_PROBLEM_INTERNAL is None or DMOJ_PDF_PROBLEM_CACHE is not None

ACE_DEFAULT_LIGHT_THEME = DMOJ_THEME_DEFAULT_ACE_THEME['light']
ACE_DEFAULT_DARK_THEME = DMOJ_THEME_DEFAULT_ACE_THEME['dark']
39 changes: 11 additions & 28 deletions judge/management/commands/render_pdf.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
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, PdfoidPDFRender, PuppeteerPDFRender, SeleniumPDFRender
from judge.utils.pdfoid import render_pdf


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('-c', '--chrome', '--puppeteer', action='store_const',
const=PuppeteerPDFRender, default=DefaultPdfMaker, dest='engine')
parser.add_argument('-S', '--selenium', action='store_const', const=SeleniumPDFRender, dest='engine')
parser.add_argument('-p', '--pdfoid', action='store_const', const=PdfoidPDFRender, dest='engine')

def handle(self, *args, **options):
try:
Expand All @@ -36,22 +27,14 @@ def handle(self, *args, **options):
except ProblemTranslation.DoesNotExist:
trans = None

directory = options['directory']
with options['engine'](directory, clean_up=directory is None) as maker, \
translation.override(options['language']):
with open(problem.code + '.pdf', 'wb') as f, 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', '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')
f.write(render_pdf(
html=get_template('problem/raw.html').render({
'problem': problem,
'problem_name': problem_name,
'description': problem.description if trans is None else trans.description,
'url': '',
}).replace('"//', '"https://').replace("'//", "'https://"),
title=problem_name,
))
Loading

0 comments on commit 5cf986b

Please sign in to comment.