Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Many fixes with error handling; Improved README.md

  • Loading branch information...
commit 4274495b04c4921f6214588195efb5240c5f5580 1 parent 6098a9e
Emir Habul authored
Showing with 130 additions and 92 deletions.
  1. +29 −2 README.md
  2. +101 −90 valodator.py
31 README.md
View
@@ -12,6 +12,17 @@ You need to have registered accounts on online judges and configure valodator.
Testing was done with PC^2 version 8 and 9. Instructions shown here are for
version 9, differences between versions 8 and 9 are minimal but they exist.
+## Prerequisites
+
+* python 2.x with mechanize and BeautifulSoup
+* PC^2 version 9 (or version 8)
+
+On ubuntu you can try something like this
+
+ sudo apt-get install python-setuptools
+ sudo easy_install mechanize
+ sudo easy_install BeautifulSoup
+
## Configuring valodator
Modify and copy config file to `/etc/` with name `valodator.config`
@@ -29,7 +40,8 @@ In the field **Execution command line** you should put something trivial like
`echo`. I chose `echo` since it is mostly harmless and is present on every
unix-like system.
-![Languages window](http://imgur.com/yTuDY.png) ![Languages tab](http://imgur.com/hsYDe.png)
+![Languages window](http://imgur.com/yTuDY.png)
+![Languages tab](http://imgur.com/hsYDe.png)
If you want to be adventurous you can try replacing compiler command with
`touch {:basename}` command, this is not important and it's completely up to
@@ -39,7 +51,9 @@ you.
To configure problems follow screenshots shown below.
-![Problem window 1](http://imgur.com/9cV1H.png) ![Problem window 2](http://imgur.com/2GNzv.png) ![Problem window 3](http://imgur.com/eQZlF.png)
+![Problem window 1](http://imgur.com/9cV1H.png)
+![Problem window 2](http://imgur.com/2GNzv.png)
+![Problem window 3](http://imgur.com/eQZlF.png)
In short, you don't need to set input/output files and you need to set
external validator to `validator.py`. In addition, **validator command line**
@@ -63,6 +77,19 @@ Value of `<website>` can be of following
This will work only until some change is made on online judge website which
will cause this validator to misbehave.
+## Known issues
+
+* Don't manually submit problems with accounts which are used by valodator.
+ This will cause "Exception: More than one status response found".
+ Can be resolved by deleting `*_skip.txt` files.
+
+## Troubleshooting
+
+Try reading logs from `logs` directory in PC^2. Also, try reading
+`executesite1judge1/valodator_calls.log`.
+
+Send logs to me or open a new issue if you are stuck.
+
## Contact
Here on github or email: [emiraga@gmail.com](mailto:emiraga@gmail.com)
191 valodator.py
View
@@ -7,17 +7,17 @@
import time
import itertools
import traceback
-import mechanize
import cookielib
import urllib
-from BeautifulSoup import BeautifulSoup
+import httplib
from ConfigParser import SafeConfigParser
import warnings
warnings.filterwarnings('ignore', '.*',)
MAX_REFRESHES = 15 # refresh until result appears
-COOKIE_FILE = '/tmp/valodator_cookies'
+COOKIE_FILE = './valodator_cookies'
+LOG_FILE = './valodator_calls.log'
parser = SafeConfigParser()
config_files = [ '/etc/valodator.config', './valodator.config' ]
@@ -41,11 +41,28 @@
LANG_CPP = 1
LANG_JAVA = 2
-with open('/tmp/valodator_calls','a') as f:
- f.write('Called:')
- for arg in sys.argv:
- f.write(' '+arg)
- f.write('\n')
+def write_status(outputfile, status):
+ """ Show judgement to PC^2.
+ We want to be able to report import errors to PC^2.
+ """
+ with open(outputfile, 'w') as f:
+ f.write('<?xml version="1.0"?>\n');
+ f.write('<result outcome="' + status +'" '
+ +'security="' + outputfile + '"></result>\n')
+
+try:
+ import mechanize
+except ImportError, e:
+ if len(sys.argv) >= 3:
+ write_status(sys.argv[2], 'Error, mechanize is not installed')
+ raise e
+
+try:
+ from BeautifulSoup import BeautifulSoup
+except ImportError, e:
+ if len(sys.argv) >= 3:
+ write_status(sys.argv[2], 'Error, BeautifulSoup is not installed')
+ raise e
def BuildBrowser(cj):
br = mechanize.Browser(factory=mechanize.RobustFactory())
@@ -61,20 +78,7 @@ def BuildBrowser(cj):
#br.set_debug_http(True)
return br
-def view_reponse(br):
- with open('/tmp/index.html','w') as f:
- f.write( br.response().read() )
- import webbrowser
- webbrowser.open_new_tab("file:///tmp/index.html")
-
-class LiveArchive(object):
- userid = parser.get('livearchive', 'userid')
- staturl = ('http://acmicpc-live-archive.uva.es/nuevoportal/status.php?u='+
- userid)
- submiturl = 'http://acmicpc-live-archive.uva.es/nuevoportal/mailer.php'
- skipfile = '/tmp/livearchive_skip.txt'
- languages = ['C', 'C++', 'Java']
-
+class OnlineJudge(object):
def __init__(self, br):
""" We will keep a list of ID's which we must skip in a file"""
self.br = br
@@ -85,7 +89,28 @@ def __init__(self, br):
self.skip = [ID for ID,status in self.getStatusList()]
with open(self.skipfile,'w') as f:
f.write( '\n'.join(self.skip)+'\n' )
- print 'Written'
+ print 'Skip file written'
+
+ def add_new_skip(self, ID):
+ """ Add new ID of problem which we skip """
+ with open(self.skipfile,'a') as f: f.write( ID + '\n')
+ self.skip.append(ID)
+
+ def cleanup_after_crash(self):
+ """ Remove skip files """
+ if os.access(self.skipfile, os.F_OK):
+ os.remove(self.skipfile)
+
+class LiveArchive(OnlineJudge):
+ userid = parser.get('livearchive', 'userid')
+ staturl = ('http://acmicpc-live-archive.uva.es/nuevoportal/status.php?u='+
+ userid)
+ submiturl = 'http://acmicpc-live-archive.uva.es/nuevoportal/mailer.php'
+ skipfile = './livearchive_skip.txt'
+ languages = ['C', 'C++', 'Java']
+
+ def __init__(self, br):
+ OnlineJudge.__init__(self, br)
def getStatusList(self, skip = False):
ret = []
@@ -121,7 +146,7 @@ def getVerdict(self, problem, language, code):
if len(l)>=2: raise Exception('More than one status response found')
if len(l) == 1:
(ID,status) = l[0]
- with open(self.skipfile,'a') as f: f.write( ID + '\n')
+ self.add_new_skip(ID)
if 'Accepted' in status: return MSG_ACCEPTED
if 'Wrong' in status: return MSG_WA
@@ -132,30 +157,21 @@ def getVerdict(self, problem, language, code):
if 'Compil' in status: return MSG_CE
if 'Output' in status: return MSG_OLE
if 'Restricted' in status: return MSG_RESTRICT
- return MSG_OTHER
+ print 'Unknown status: ' + status
print '.'
time.sleep(1)
raise Exception('Status response did not arrive after many retries')
-class TjuOnlineJudge(object):
+class TjuOnlineJudge(OnlineJudge):
username = parser.get('tju','username')
password = parser.get('tju','password')
staturl = 'http://acm.tju.edu.cn/toj/status.php?user=' + username
submiturl = 'http://acm.tju.edu.cn/toj/submit_process.php'
- skipfile = '/tmp/tju_skip.txt'
+ skipfile = './tju_skip.txt'
languages = ['0', '1', '2'] # C, C++, Java
def __init__(self, br):
- """ We will keep a list of ID's which we must skip in a file"""
- self.br = br
- if os.access(self.skipfile, os.F_OK):
- with open(self.skipfile) as f:
- self.skip = [a.strip() for a in f]
- else:
- self.skip = [ID for ID,status in self.getStatusList()]
- with open(self.skipfile,'w') as f:
- f.write( '\n'.join(self.skip)+'\n' )
- print 'Written'
+ OnlineJudge.__init__(self, br)
def getStatusList(self, skip = False):
ret = []
@@ -191,7 +207,7 @@ def getVerdict(self, problem, language, code):
if len(l)>=2: raise Exception('More than one status response found')
if len(l) == 1:
(ID,status) = l[0]
- with open(self.skipfile,'a') as f: f.write( ID + '\n')
+ self.add_new_skip(ID)
if 'Accepted' in status: return MSG_ACCEPTED
if 'Wrong' in status: return MSG_WA
if 'Presentation' in status: return MSG_PE
@@ -201,30 +217,21 @@ def getVerdict(self, problem, language, code):
if 'Compil' in status: return MSG_CE
if 'Output' in status: return MSG_OLE
if 'Restricted' in status: return MSG_RESTRICT
- return MSG_OTHER
+ print 'Unknown status: ' + status
print '.'
time.sleep(1)
raise Exception('Status response did not arrive after many retries')
-class TimusOnlineJudge(object):
+class TimusOnlineJudge(OnlineJudge):
userid = parser.get('timus','userid')
usernumber = parser.get('timus','usernumber')
staturl = 'http://acm.timus.ru/status.aspx?author='+usernumber
submiturl = 'http://acm.timus.ru/submit.aspx'
- skipfile = '/tmp/timus_skip.txt'
+ skipfile = './timus_skip.txt'
languages = ['9', '10', '7'] # C, C++, Java
def __init__(self, br):
- """ We will keep a list of ID's which we must skip in a file"""
- self.br = br
- if os.access(self.skipfile, os.F_OK):
- with open(self.skipfile) as f:
- self.skip = [a.strip() for a in f]
- else:
- self.skip = [ID for ID,status in self.getStatusList()]
- with open(self.skipfile,'w') as f:
- f.write( '\n'.join(self.skip)+'\n' )
- print 'Written'
+ OnlineJudge.__init__(self, br)
def getStatusList(self, skip = False):
ret = []
@@ -261,7 +268,7 @@ def getVerdict(self, problem, language, code):
if len(l)>=2: raise Exception('More than one status response found')
if len(l) == 1:
(ID,status) = l[0]
- with open(self.skipfile,'a') as f: f.write( ID + '\n')
+ self.add_new_skip(ID)
if 'Accepted' in status: return MSG_ACCEPTED
if 'Wrong' in status: return MSG_WA
if 'Presentation' in status: return MSG_PE
@@ -271,12 +278,12 @@ def getVerdict(self, problem, language, code):
if 'Compil' in status: return MSG_CE
if 'Output' in status: return MSG_OLE
if 'Restricted' in status: return MSG_RESTRICT
- return MSG_OTHER
+ print 'Unknown status: ' + status
print '.'
time.sleep(1)
raise Exception('Status response did not arrive after many retries')
-class UvaOnlineJudge(object):
+class UvaOnlineJudge(OnlineJudge):
username = parser.get('uva','username')
password = parser.get('uva','password')
loginurl = 'http://uva.onlinejudge.org/'
@@ -285,19 +292,10 @@ class UvaOnlineJudge(object):
staturl = ('http://uva.onlinejudge.org/'+
'index.php?option=com_onlinejudge&Itemid=9')
languages = ['1', '3', '2'] # C, C++, Java
- skipfile = '/tmp/uva_skip.txt'
+ skipfile = './uva_skip.txt'
def __init__(self, br):
- """ We will keep a list of ID's which we must skip in a file"""
- self.br = br
- if os.access(self.skipfile, os.F_OK):
- with open(self.skipfile) as f:
- self.skip = [a.strip() for a in f]
- else:
- self.skip = [ID for ID,status in self.getStatusList()]
- with open(self.skipfile,'w') as f:
- f.write( '\n'.join(self.skip)+'\n' )
- print 'Written'
+ OnlineJudge.__init__(self, br)
def login(self):
print 'opening front page'
@@ -364,7 +362,7 @@ def getVerdict(self, problem, language, code, retry=True):
if len(l)>=2: raise Exception('More than one status response found')
if len(l) == 1:
(ID,status) = l[0]
- with open(self.skipfile,'a') as f: f.write( ID + '\n')
+ self.add_new_skip(ID)
if 'Accepted' in status: return MSG_ACCEPTED
if 'Wrong' in status: return MSG_WA
if 'Presentation' in status: return MSG_PE
@@ -412,47 +410,60 @@ def formatExceptionInfo(level = 6):
error_value) + '\n'.join(tb_list)
if __name__ == '__main__':
+ with open(LOG_FILE,'a') as f:
+ f.write('Called:')
+ for arg in sys.argv:
+ f.write(' '+arg)
+ f.write('\n')
print 'valodator running...'
try:
if len(sys.argv) < 4: raise Exception('Too few arguments')
- """ valodator.py code.cpp ans.xml uva/10055 """
+
with open(sys.argv[1]) as f: code = f.read()
outputfile = sys.argv[2]
url = sys.argv[3]
website, problem = recognize_url(url)
+ website = website.lower()
language = recognize_language(sys.argv[1])
cjar = mechanize.LWPCookieJar()
if os.access(COOKIE_FILE, os.F_OK): cjar.load(COOKIE_FILE)
- browser = BuildBrowser(cjar)
-
- website = website.lower()
- print 'Website is ' + website
- if website == 'livearchive' or website == 'live-archive':
- web = LiveArchive(browser)
- elif website == 'uva':
- web = UvaOnlineJudge(browser)
- elif website == 'tju':
- web = TjuOnlineJudge(browser)
- elif website == 'timus':
- web = TimusOnlineJudge(browser)
- else:
- raise Exception('Website not recognized');
-
- status = web.getVerdict(problem, language, code)
+ # some weird exception pops up due to server's error
+ for retry in xrange(10):
+ try:
+ browser = BuildBrowser(cjar)
+ print 'Website is ' + website
+ if website == 'livearchive' or website == 'live-archive':
+ web = LiveArchive(browser)
+ elif website == 'uva':
+ web = UvaOnlineJudge(browser)
+ elif website == 'tju':
+ web = TjuOnlineJudge(browser)
+ elif website == 'timus':
+ web = TimusOnlineJudge(browser)
+ else:
+ raise Exception('Website not recognized');
+ status = web.getVerdict(problem, language, code)
+ break
+ except httplib.HTTPException, e: #BadStatusLine, IncompleteRead
+ web.cleanup_after_crash()
+ s = formatExceptionInfo()
+ print s
+ with open(LOG_FILE, 'a') as f:
+ f.write('Exception: ' + s + '\n' )
+ if retry == 9:
+ write_status(outputfile, "Error, many HTTPExceptions")
+ sys.exit(1)
cjar.save(COOKIE_FILE, ignore_discard=True, ignore_expires=True )
-
- with open(outputfile, 'w') as f:
- f.write('<?xml version="1.0"?>\n');
- f.write('<result outcome="' + status +'" '
- +'security="' + outputfile + '"></result>\n')
- except:
+ write_status(outputfile, status)
+ except Exception, e: #gotta catch 'em all
s = formatExceptionInfo()
print s
- with open('/tmp/valodator_calls','a') as f:
+ with open(LOG_FILE, 'a') as f:
f.write('Exception: ' + s + '\n' )
+ write_status(outputfile, "Exception: " + str(e))
Please sign in to comment.
Something went wrong with that request. Please try again.