Index: manage.py =================================================================== --- manage.py (revision 13) +++ manage.py (working copy) @@ -1,5 +1,6 @@ #!/usr/bin/env python from django.core.management import execute_manager +from evaluator.main import * try: import settings # Assumed to be in the same directory. except ImportError: @@ -8,4 +9,7 @@ sys.exit(1) if __name__ == "__main__": + evaluator = Client() + evaluator.start() execute_manager(settings) + Index: evaluator/settings.py =================================================================== --- evaluator/settings.py (revision 0) +++ evaluator/settings.py (revision 0) @@ -0,0 +1,13 @@ +# '%' is not allowed in the compilation string. +# Format Strings: +# %i --- Input File Name +# %o --- Ouput File Name +# %u --- User Name (who submitted the code) +# %q --- Question Name + + +C_COMPILE_STR = 'gcc -lm \'%i\' -o \'%o\'' +CPP_COMPILE_STR = 'g++ -lm \%i\' -o \'%o\'' +JAVA_COMPILE_STR = 'javac \'%i\'' + +WORK_DIR = 'evaluator/work_files' Index: evaluator/main.py =================================================================== --- evaluator/main.py (revision 0) +++ evaluator/main.py (revision 0) @@ -0,0 +1,196 @@ +import sys +import os +import time +import commands +import threading +import subprocess +import tempfile +import time +try: + from evaluator.settings import * + from server.models import Attempt, ToBeEvaluated, BeingEvaluated +except ImportError: + C_COMPILE_STR = 'gcc -lm \'%i\' -o \'%o\'' + CPP_COMPILE_STR = 'g++ -lm \%i\' -o \'%o\'' + JAVA_COMPILE_STR = 'javac \'%i\'' + WORK_DIR = 'evaluator/work_files' + + +class EvaluatorError (Exception): + def __init__ (self, error, value=''): + self.value = value + self.error = error + + def __str__ (self): + return repr (self.value) + + +class Evaluator: + def __str__ (self): + raise NotImplementedError ('Must be Overridden') + + def compile (self, code_file, input_file): + raise NotImplementedError ('Must be Overridden') + + def run (self, exec_file): + raise NotImplementedError ('Must be Overridden') + + def save_file (self, file_path, contents): + try: + if not os.path.exists (WORK_DIR): + os.mkdir (WORK_DIR) + + path_cont = [] + path_split = lambda (x): x != '' and \ + path_split(os.path.split(x)[0]) or \ + path_cont.append (os.path.basename(x)) + path_cont = path_cont[1:-1] + path = WORKDIR + for cont in path_cont: + path = os.path.join (path, cont) + if not os.path.exists (path): + os.mkdir (path) + + open_file = open (os.path.join (WORK_DIR, file_path), 'w') + open_file.write (contents) + open_file.close() + except OSError, IOError: + raise + return file_name + + def check (self, attempt, output): + raise NotImplementedError ('Must be Overridden') + + def evaluate (self, attempt): + # Save the File + save_loc = os.path.join (attempt.user.username, attempt.filename) + code_file = self.save_file (save_loc, attempt.code) + + # Compile the File + exec_file = self.compile (code_file, attempt.question.test_input) + + # Execute the file for preset input + output = self.run (exec_file) + + # Match the output to expected output + return self.check (attempt, output) + + +class C_Evaluator (Evaluator): + num_out = 0 + status = 0 + output = '' + def __init__ (self): + threading.Thread.__init__ (self) + self.compile_cmd = C_COMPILE_STR + + + def __str__ (self): + return 'C Evaluator' + + def compile (self, code_file): + output_file = code_file # Change this value to change output file name + # replace the code with the object file + cmd = self.compile_cmd.replace ('%i',code_file).replace('%o',output_file) + + (status, output) = commands.getstatusoutput (cmd) + if status != 0: + raise EvaluatorError ('Compiler Error', output) + else: + return output_file + + def run (self, exec_file, input_file): + output_file = tempfile.NameTemporaryFile () + kws = {} + cmd = exec_file + ' < ' + input_file + ' > ' + output_file.name + start_time = time.time() + p = subproces.Popen (cmd, **kws) + # (status, output) = commands.getstatusoutput (exec_file + ' < ' \ + # + input_file) + while True: + if time.time() - start_time >= 5: + raise EvalutorError ('Time Limit Expired') + elif p.poll() != None: + break + time.sleep (0.5) + if p.returncode != 0: + raise EvaluatorError ('Run-Time Error') + else: + return output + + def check (self, attempt, output): + pass + +class CPP_Evaluator (Evaluator): + def __str__ (self): + return 'C++ Evaluator' + def evaluate (self, attempt): + pass + +class Java_Evaluator (Evaluator): + def __str__ (self): + return 'Java Evaluator' + def evaluate (self, attempt): + pass + +class PythonEvaluator (Evaluator): + def __str__ (self): + return 'Python Evaluator' + def evaluate (self, attempt): + pass + +class Client (thread.Threading): + """ The Evaluator will evaluate and update the status """ + evaluators = {'c':C_Evaluator, 'c++':CPP_Evaluator, + 'java':Java_Evaluator, 'python':Python_Evaluator} + + def __init__ (self): + pass + + def queue_not_empty (self): + """ Checks if the ToBeEvaluated queue is empty or not """ + if ToBeEvaluated.objects.count > 0: + return True + return False + + def dequeue_attempts (self): + """ Returns an attempt from the ToBeEvaluated queue based on priority + algorithms or return None if queue is empty """ + try: + attempt = ToBeEvaluated.objects.all()[0] + to_be_eval = BeingEvaluated(attempt=attempt.attempt) + attempt.delete() + return to_be_eval + except IndexError: + return None + + def get_attempt (self): + """ Keep polling the ToBeEvaluated queue until an attempt to be + evaluated is obtained """ + while True: + if (self.queue_not_empty()): + return self.dequeue_attempts() + time.sleep (1) + + def evaluate (self, attempt): + """ Evaluate the attempt and return the ruling :-) """ + lang = attempt.language.lang.lower() + # first list special case languages whose names cannot be used for + # function names in python + try: + evaluator = self.evaluators[lang](attempt) + except KeyError: + raise NotImplementedError ('Language '+lang+' not supported') + result = evaluator.evaluate() + + def run (self): + print 'Evaluator Started' + while True: + attempt = self.get_attempt () + if attempt == None: + continue + # evalute the attempt + return_value = self.evaluate (attempt.attempt) + attempt.delete () + return return_value + Index: server/models.py =================================================================== --- server/models.py (revision 13) +++ server/models.py (working copy) @@ -7,6 +7,7 @@ # TODO: Change this to self.name and have special method for activation key def __str__(self): return self.user.get_full_name() + score = models.PositiveIntegerField(default=0) activation_key = models.CharField(maxlength=40) key_expires = models.DateTimeField() @@ -17,6 +18,7 @@ """ Contains the question content and path to evaluators and test cases """ def __str__(self): return self.name + name = models.CharField(maxlength=32) text = models.TextField() test_input = models.FileField(upload_to = 'hidden/evaluators/testCases') @@ -28,13 +30,15 @@ class Language(models.Model): def __str__(self): return self.compiler - compiler = models.CharField(maxlength=32) + + lang = models.CharField(maxlength=32) class Admin: pass class Attempt(models.Model): """ Contains Attempt Information """ def __str__(self): - return self.user.user.username + ' :' + self.question.name + return self.user.user.username + ' :' + self.question.name + user = models.ForeignKey(UserProfile) question = models.ForeignKey(Question) result = models.BooleanField() @@ -43,5 +47,12 @@ time_of_submit = models.DateTimeField(auto_now_add=True) class Admin: pass -class Pending(models.Model): +class ToBeEvaluated(models.Model): + """ Contains Attempts which are yet to be Evaluated """ attempt = models.ForeignKey(Attempt) + +class BeingEvaluated (models.Model): + """ Contains Attempts which have been assigned to an Evaluator but whose + evaluation process is yet to be completed. In the case that an evaluator + crashes, these attempt might need to be moved back to ToBeEvaluated """ + attempt = models.ForeignKey (Attempt) Index: settings.py =================================================================== --- settings.py (revision 13) +++ settings.py (working copy) @@ -86,5 +86,9 @@ CONTEST_NAME = 'Hackzor' # replace with the email of your own contest CONTEST_EMAIL = 'hackzor@googlegroups.com' + # replace with the url of your own contest CONTEST_URL = '192.168.1.2' + +# Supported Languages for the code submission +SUPPORTED_LANGS = ['C','C++','Java','Python','Ruby'] Index: ChangeLog =================================================================== --- ChangeLog (revision 13) +++ ChangeLog (working copy) @@ -6,6 +6,8 @@ * settings.py (CONTEST_NAME, CONTEST_EMAIL): Added generic support for Contest Name and Email ID + (SUPPORTED_LANGS): Added SUPPORTED_LANGS, which should be used to + populate the language field in code submission 2006-10-16 Ravi Shankar * urls.py(urlpatterns): Added default home page