source: trunk/eval/eval @ 1184

Revision 1183, 4.4 KB checked in by bogdan2412, 4 weeks ago (diff)

Refactor eval code in preparation for new task types.

Previously, our eval code consisted of a 300 line method in
classic_grader.php, which was painful. :(

I've split the functionality into separate methods inside a BaseGrader?
and ClassicGrader? class. BaseGrader? provides functionality which is
common for all task types such as setting up a jail, compiling
evaluators, handling test groups, judging outputs (via diff or custom
evaluator), etc. Every different type of task will require a new class
which extends the BaseGrader? and implements the method testCaseJudge
which will contain the task type's specific behaviour. Other methods
in BaseGrader? can be subclassed as well: for example, for output only
tasks one would replace the 'compile user source code' method with a
'extract user outputs' method.

REVIEW URL: http://reviewboard.infoarena.ro/r/205/

  • Property svn:executable set to *
Line 
1#! /usr/bin/env php
2<?php
3
4require_once(dirname($argv[0]) . "/config.php");
5
6require_once(IA_ROOT_DIR.'common/log.php');
7require_once(IA_ROOT_DIR.'common/common.php');
8
9require_once(IA_ROOT_DIR.'common/score.php');
10require_once(IA_ROOT_DIR.'common/task.php');
11require_once(IA_ROOT_DIR.'common/round.php');
12require_once(IA_ROOT_DIR.'common/security.php');
13require_once(IA_ROOT_DIR.'common/db/task.php');
14require_once(IA_ROOT_DIR.'common/db/job.php');
15require_once(IA_ROOT_DIR.'common/db/user.php');
16
17require_once(IA_ROOT_DIR.'eval/utilities.php');
18require_once(IA_ROOT_DIR.'eval/download.php');
19require_once(IA_ROOT_DIR.'eval/ClassicGrader.php');
20
21error_reporting(0xFFFF);
22
23// Send job result.
24function job_send_result($jobid, $jobresult) {
25    log_print("Sending result for job #{$jobid} ".
26            "score {$jobresult['score']} message {$jobresult['message']}");
27    log_print("");
28    job_update($jobid, 'done', $jobresult['message'], $jobresult['log'], $jobresult['score']);
29}
30
31// This function handles a certain job.
32function job_handle($job) {
33    // Evaluating, mark as processing.
34    // FIXME: do it in query.
35    job_update($job['id'], 'processing');
36
37    // Get task
38    $task = task_get($job['task_id']);
39    log_assert(!is_null($task), "Nu am gasit task-ul " . $job['task_id']);
40    log_assert_valid(task_validate($task));
41
42    // Get task parameters.
43    $task_parameters = task_get_parameters($job['task_id']);
44    log_assert(!is_null($task_parameters),
45            "Nu am putut paramtetrii task-ul " . $job['task_id']);
46    log_assert_valid(task_validate_parameters($task['type'],
47                                              $task_parameters));
48
49    // Make the grader and execute it.
50    log_print("Job #{$job['id']} task {$task['id']} round {$job['round_id']} type {$task['type']}");
51    if ($task['type'] == 'classic') {
52        $grader = new ClassicGrader($task, $task_parameters, $job);
53    } else {
54        log_error("Nu stiu sa evaluez task-uri de tip ".$task['type']);
55    }
56    $job_result = $grader->grade();
57
58    $submit_count = 0;
59    /**
60     * Check if the task was in a penalty type round
61     * Also increase the submit_count
62     */
63    if (!is_null($job['round_id']) && $job['round_id'] !== '') {
64        $round = round_get($job['round_id']);
65        if ($round['type'] == 'penalty-round') {
66            $round_parameters = round_get_parameters($job['round_id']);
67            /*
68            * Check the number of submits (so we can add to penalty)
69            */
70            if ($job_result['score'] > 0) {
71                $percent = max($round_parameters['minimum_score'], 100 -
72                        $job['submissions'] * $round_parameters['submit_cost'] -
73                        (int)((db_date_parse($job['submit_time']) -
74                            db_date_parse($round['start_time'])) /
75                        $round_parameters['decay_period']));
76                $job_result['score'] = (int)($percent * $job_result['score']
77                        / 100);
78            }
79        }
80    }
81
82    // Send job result and update score magic.
83    job_send_result($job['id'], $job_result);
84    score_update_for_job(
85            $job_result['score'],
86            $job['submit_time'],
87            $job['user_id'],
88            $job['task_id'],
89            $job['round_id']);
90}
91
92// Main loop. It works by polling the sql server
93// every IA_judge_POLL_INTERVAL
94function judge_main_loop() {
95    db_connect();
96
97    log_print("");
98    log_print("Judge started");
99    log_print("");
100
101    while (1) {
102        if ($round = round_get_round_to_wait()) {
103            round_event_wait($round);
104            continue;
105        }
106        if ($round = round_get_round_to_start()) {
107            round_event_start($round);
108            continue;
109        }
110        if ($round = round_get_round_to_stop()) {
111            round_event_stop($round);
112            continue;
113        }
114        if ($job = job_get_next_job()) {
115            job_handle($job);
116            continue;
117        }
118        milisleep(IA_JUDGE_POLL_INTERVAL);
119    }
120}
121
122// Parse options. FIXME: php's built-in getopt is dumb
123$options = getopt("dl:");
124
125if (array_key_exists('d', $options)) {
126    // Run in daemon mode. Fork to background, pid to eval.pid
127    if ($pid = pcntl_fork()) {
128        die();
129    }
130    file_put_contents(IA_ROOT_DIR . 'eval/eval.pid', getmypid() . "\n");
131}
132
133// FIXME: Is there a better way to redirect output? No freopen in php.
134if (array_key_exists('l', $options)) {
135    ini_set('error_log', $options['l']);
136}
137
138judge_main_loop();
139
140?>
Note: See TracBrowser for help on using the repository browser.