1669 lines
59 KiB
PHP
1669 lines
59 KiB
PHP
<?PHP
|
|
|
|
// $Id: lib.php,v 1.1 2012/07/10 05:37:08 sunshine Exp $
|
|
/// Library of functions and constants for module programming
|
|
/// (replace programming with the name of your module and delete this line)
|
|
require_once($CFG->dirroot . '/lib/datalib.php');
|
|
|
|
define('PROGRAMMING_STATUS_NEW', 0);
|
|
define('PROGRAMMING_STATUS_COMPILING', 1);
|
|
define('PROGRAMMING_STATUS_COMPILEOK', 2);
|
|
define('PROGRAMMING_STATUS_RUNNING', 3);
|
|
define('PROGRAMMING_STATUS_WAITING', 4);
|
|
define('PROGRAMMING_STATUS_FINISH', 10);
|
|
define('PROGRAMMING_STATUS_COMPILEFAIL', 11);
|
|
define('PROGRAMMING_MAX_ATTEMPTS', 6);
|
|
|
|
define('PROGRAMMING_SHOWMODE_NORMAL', 1);
|
|
define('PROGRAMMING_SHOWMODE_CONTEST', 2);
|
|
|
|
define('PROGRAMMING_RESEMBLE_DELETED', -1);
|
|
define('PROGRAMMING_RESEMBLE_NEW', 0);
|
|
define('PROGRAMMING_RESEMBLE_WARNED', 1);
|
|
define('PROGRAMMING_RESEMBLE_CONFIRMED', 2);
|
|
define('PROGRAMMING_RESEMBLE_FLAG1', 11);
|
|
define('PROGRAMMING_RESEMBLE_FLAG2', 12);
|
|
define('PROGRAMMING_RESEMBLE_FLAG3', 13);
|
|
|
|
define('PROGRAMMING_TEST_SHOWAFTERDISCOUNT', -2);
|
|
define('PROGRAMMING_TEST_HIDDEN', -1);
|
|
define('PROGRAMMING_TEST_SHOWINRESULT', 0);
|
|
define('PROGRAMMING_TEST_SHOW', 1);
|
|
|
|
define('PROGRAMMING_PRESET_BEGIN', "/* PRESET CODE BEGIN - NEVER TOUCH CODE BELOW */");
|
|
define('PROGRAMMING_PRESET_END', "/* PRESET CODE END - NEVER TOUCH CODE ABOVE */");
|
|
|
|
define('PROGRAMMING_RANGE_ALL', 0);
|
|
define('PROGRAMMING_RANGE_LATEST', 1);
|
|
|
|
function programming_add_instance($programming) {
|
|
/// Given an object containing all the necessary data,
|
|
/// (defined by the form in mod.html) this function
|
|
/// will create a new instance and return the id number
|
|
/// of the new instance.
|
|
global $DB;
|
|
|
|
$programming->timemodified = time();
|
|
|
|
if ($programming->inputs == 0)
|
|
$programming->inputfile = '';
|
|
if ($programming->outputs == 0)
|
|
$programming->outputfile = '';
|
|
$id = $DB->insert_record('programming', $programming);
|
|
|
|
if (isset($programming->langlimit) && is_array($programming->langlimit)) {
|
|
foreach ($programming->langlimit as $lang) {
|
|
$pl = new stdClass();
|
|
$pl->programmingid = $id;
|
|
$pl->languageid = $lang;
|
|
$DB->insert_record('programming_langlimit', $pl);
|
|
}
|
|
}
|
|
if (isset($programming->category) && is_array($programming->category)) {
|
|
foreach ($programming->category as $cat) {
|
|
$pc = new stdClass();
|
|
$pc->pid = $id;
|
|
$pc->catid = $cat;
|
|
$DB->insert_record('programming_catproblemmap', $pc);
|
|
}
|
|
}
|
|
global $CFG;
|
|
if (!function_exists('grade_update')) { //workaround for buggy PHP versions
|
|
require_once($CFG->libdir . '/gradelib.php');
|
|
}
|
|
$params = array('itemname' => $programming->name,
|
|
'grademax' => $programming->grade,
|
|
'grademin' => 0,
|
|
'gradetype' => GRADE_TYPE_VALUE);
|
|
grade_update('mod/programming', $programming->course, 'mod', 'programming', $id, 0, NULL, $params);
|
|
return $id;
|
|
}
|
|
|
|
function programming_update_instance($programming) {
|
|
/// Given an object containing all the necessary data,
|
|
/// (defined by the form in mod.html) this function
|
|
/// will update an existing instance with new data.
|
|
global $DB;
|
|
|
|
$programming->timemodified = time();
|
|
$programming->id = $programming->instance;
|
|
|
|
if ($programming->inputs == 0)
|
|
$programming->inputfile = '';
|
|
if ($programming->outputs == 0)
|
|
$programming->outputfile = '';
|
|
|
|
if (isset($programming->keeplatestonly) and $programming->keeplatestonly) {
|
|
programming_delete_old_submits($programming->id);
|
|
}
|
|
|
|
// Update the langlimit table
|
|
$changed = false;
|
|
if (isset($programming->langlimit) && is_array($programming->langlimit)) {
|
|
$newlangs = $programming->langlimit;
|
|
} else {
|
|
$newlangs = array();
|
|
}
|
|
|
|
$langs = array();
|
|
$rows = $DB->get_records('programming_langlimit', array('programmingid' => $programming->id));
|
|
if (is_array($rows)) {
|
|
foreach ($rows as $row) {
|
|
$langs[] = $row->languageid;
|
|
}
|
|
}
|
|
|
|
foreach (array_diff($langs, $newlangs) as $lang) {
|
|
$DB->delete_records('programming_langlimit', array('programmingid' => $programming->id, 'languageid' => $lang));
|
|
$changed = true;
|
|
}
|
|
|
|
foreach (array_diff($newlangs, $langs) as $lang) {
|
|
$pl = new stdClass();
|
|
$pl->programmingid = $programming->id;
|
|
$pl->languageid = $lang;
|
|
$DB->insert_record('programming_langlimit', $pl);
|
|
$changed = true;
|
|
}
|
|
|
|
if (isset($programming->category) && is_array($programming->category)) {
|
|
$newcats = $programming->category;
|
|
} else {
|
|
$newcats = array();
|
|
}
|
|
|
|
$cats = array();
|
|
$rows1 = $DB->get_records('programming_catproblemmap', array('pid' => $programming->id));
|
|
if (is_array($rows1)) {
|
|
foreach ($rows1 as $row) {
|
|
$cats[] = $row->catid;
|
|
}
|
|
}
|
|
|
|
foreach (array_diff($cats, $newcats) as $cat) {
|
|
$DB->delete_records('programming_catproblemmap', array('pid' => $programming->id, 'catid' => $cat));
|
|
$changed = true;
|
|
}
|
|
|
|
foreach (array_diff($newcats, $cats) as $cat) {
|
|
$pc = new stdClass();
|
|
$pc->pid = $programming->id;
|
|
$pc->catid = $cat;
|
|
$DB->insert_record('programming_catproblemmap', $pc);
|
|
$changed = true;
|
|
}
|
|
global $CFG;
|
|
if (!function_exists('grade_update')) { //workaround for buggy PHP versions
|
|
require_once($CFG->libdir . '/gradelib.php');
|
|
}
|
|
$params = array('itemname' => $programming->name,
|
|
'grademax' => $programming->grade,
|
|
'grademin' => 0,
|
|
'gradetype' => GRADE_TYPE_VALUE);
|
|
grade_update('mod/programming', $programming->course, 'mod', 'programming', $programming->id, 0, NULL, $params);
|
|
return $DB->update_record('programming', $programming) or $changed;
|
|
}
|
|
|
|
function programming_delete_instance($id) {
|
|
/// Given an ID of an instance of this module,
|
|
/// this function will permanently delete the instance
|
|
/// and any data that depends on it.
|
|
global $DB;
|
|
|
|
if (!$programming = $DB->get_record('programming', array('id' => $id))) {
|
|
return false;
|
|
}
|
|
|
|
$result = true;
|
|
|
|
//判断是否有引用该实例 如果没有则删除
|
|
$sql = "SELECT
|
|
* FROM {modules} m,{course_modules} cm
|
|
WHERE m.id=cm.module AND m.name='programming' AND cm.instance=?";
|
|
$params = array($programming->id);
|
|
$ps = $DB->get_records_sql($sql, $params);
|
|
if (count($ps) <= 1) {
|
|
|
|
# Delete any dependent records here #
|
|
if (!$DB->delete_records('programming_langlimit', array('programmingid' => $programming->id))) {
|
|
$result = false;
|
|
}
|
|
|
|
if (!$DB->delete_records('programming', array('id' => $programming->id))) {
|
|
$result = false;
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
function programming_user_outline($course, $user, $mod, $programming) {
|
|
/// Return a small object with summary information about what a
|
|
/// user has done with a given particular instance of this module
|
|
/// Used for user activity reports.
|
|
/// $return->time = the time they did it
|
|
/// $return->info = a short text description
|
|
|
|
global $DB;
|
|
|
|
$return = new stdClass();
|
|
$params = array($programming->id, $user->id);
|
|
$sql = "SELECT ps.*
|
|
FROM {programming_submits} AS ps,
|
|
{programming_result} AS pr
|
|
WHERE pr.programmingid = ?
|
|
AND pr.userid = ?
|
|
AND pr.latestsubmitid = ps.id";
|
|
$ps = $DB->get_records_sql($sql, $params);
|
|
|
|
if (!empty($ps)) {
|
|
$ps = array_shift($ps);
|
|
if ($programming->showmode == PROGRAMMING_SHOWMODE_NORMAL) {
|
|
if ($ps->status == PROGRAMMING_STATUS_FINISH) {
|
|
$return->info = programming_get_test_results_short($ps);
|
|
} else {
|
|
$return->info = programming_get_submit_status_short($ps);
|
|
}
|
|
} else {
|
|
if ($ps->status == PROGRAMMING_STATUS_FINISH) {
|
|
$results = $DB->get_records('programming_test_results', array('submitid' => $ps->id));
|
|
$return->info = programming_contest_get_judgeresult($results);
|
|
} else if ($ps->status == PROGRAMMING_STATUS_COMPILEFAIL) {
|
|
$return->info = get_string('CE', 'programming');
|
|
}
|
|
}
|
|
$return->time = $ps->timemodified;
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
function programming_user_complete($course, $user, $mod, $programming) {
|
|
/// Print a detailed representation of what a user has done with
|
|
/// a given particular instance of this module, for user activity reports.
|
|
|
|
return true;
|
|
}
|
|
|
|
function programming_print_recent_activity($course, $isteacher, $timestart) {
|
|
/// Given a course and a time, this module should find recent activity
|
|
/// that has occurred in programming activities and print it out.
|
|
/// Return true if there was output, or false is there was none.
|
|
|
|
global $CFG;
|
|
|
|
return false; // True if anything was printed, otherwise false
|
|
}
|
|
|
|
function programming_cron() {
|
|
/// Function to be run periodically according to the moodle cron
|
|
/// This function searches for things that need to be done, such
|
|
/// as sending out mail, toggling flags etc ...
|
|
|
|
global $CFG;
|
|
|
|
return true;
|
|
}
|
|
|
|
function programming_grades($programmingid) {
|
|
/// Must return an array of grades for a given instance of this module,
|
|
/// indexed by user. It also returns a maximum allowed grade.
|
|
///
|
|
/// $return->grades = array of grades;
|
|
/// $return->maxgrade = maximum allowed grade;
|
|
///
|
|
/// return $return;
|
|
|
|
global $CFG, $DB;
|
|
|
|
$return = new stdClass;
|
|
$programming = $DB->get_record('programming', array('id' => $programmingid));
|
|
$return->maxgrade = $programming->grade; // find the maximum allowed grade;
|
|
$return->grades = array();
|
|
|
|
// get the summary of weight of all the test
|
|
$select = 'programmingid = :pid';
|
|
$params = array('pid' => $programmingid);
|
|
$total_weight = $DB->get_field_select('block_rss_client', 'SUM(weight)', $select, $params);
|
|
$total_weight = $total_weight ? 1.0 / $total_weight : 0;
|
|
|
|
// get weight summary of each submit
|
|
$params = array($programming->grade, $total_weight, $programming->discount,
|
|
$programmingid, $programmingid, $programmingid, $programming->timediscount,
|
|
$programmingid, $programmingid);
|
|
|
|
$query = "
|
|
SELECT w.userid AS userid,
|
|
round(? * w.weight * ? * (1 - d.discount * (1 - ? / 10.0)), 2) AS grade
|
|
FROM (
|
|
SELECT s.userid AS userid,
|
|
SUM(tr.passed * t.weight) AS weight
|
|
FROM {programming_result} AS r,
|
|
{programming_submits} AS s,
|
|
{programming_test_results} AS tr,
|
|
{programming_tests} AS t
|
|
WHERE r.programmingid = ?
|
|
AND s.programmingid = ?
|
|
AND t.programmingid = ?
|
|
AND r.latestsubmitid=s.id
|
|
AND s.id=tr.submitid
|
|
AND tr.testid=t.id
|
|
GROUP BY s.userid) AS w, (
|
|
SELECT s.userid AS userid,
|
|
s.timemodified > ? AS discount
|
|
FROM {programming_result} AS r,
|
|
{programming_submits} AS s
|
|
WHERE r.programmingid = ?
|
|
AND s.programmingid = ?
|
|
AND r.latestsubmitid=s.id
|
|
GROUP BY s.userid) AS d
|
|
WHERE d.userid = w.userid
|
|
";
|
|
//print $query;
|
|
$grades = $DB->get_records_sql($query, $params);
|
|
|
|
if (is_array($grades)) {
|
|
foreach ($grades as $grade) {
|
|
$return->grades[$grade->userid] = $grade->grade;
|
|
}
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Update the grade of a submission.
|
|
*
|
|
* @param $submitid ID of submission.
|
|
*/
|
|
function programming_update_grade($submitid) {
|
|
global $CFG, $DB;
|
|
|
|
$submit = $DB->get_record('programming_submits', array('id' => $submitid));
|
|
if (empty($submit))
|
|
return;
|
|
|
|
# do not update grade if this is not the latest submission
|
|
$rst = $DB->get_record('programming_result', array('programmingid' => $submit->programmingid, 'userid' => $submit->userid));
|
|
if ($rst->latestsubmitid != $submitid)
|
|
return;
|
|
|
|
$p = $DB->get_record('programming', array('id' => $submit->programmingid));
|
|
if (empty($p))
|
|
return;
|
|
|
|
$grade = 0;
|
|
if ($submit->judgeresult != 'CE') {
|
|
// get the summary of weight of all the test
|
|
$total_weight = $DB->get_field_select('programming_tests', 'SUM(weight)', 'programmingid=?', array($submit->programmingid));
|
|
if ($total_weight == 0)
|
|
$total_weight = 1;
|
|
|
|
$sql = "SELECT SUM(pt.weight) AS weight
|
|
FROM {programming_test_results} AS ptr,
|
|
{programming_tests} AS pt
|
|
WHERE ptr.submitid = ?
|
|
AND ptr.passed = 1
|
|
AND pt.programmingid = ?
|
|
AND ptr.testid = pt.id";
|
|
$weight = $DB->get_field_sql($sql, array($submit->id, $submit->programmingid));
|
|
$grade = $p->grade * $weight / $total_weight;
|
|
if ($submit->timemodified > $p->timediscount) {
|
|
$grade = $grade * $p->discount / 10.0;
|
|
}
|
|
}
|
|
programming_grade_item_update($p, $submit, $grade);
|
|
}
|
|
|
|
function programming_grade_item_update($programming, $submit, $grade) {
|
|
global $CFG;
|
|
|
|
if (!function_exists('grade_update')) { //workaround for buggy PHP versions
|
|
require_once($CFG->libdir . '/gradelib.php');
|
|
}
|
|
|
|
$cm = get_coursemodule_from_instance('programming', $programming->id, $programming->course);
|
|
|
|
$params = array('itemname' => $programming->name,
|
|
'idnumber' => $cm->id,
|
|
'grademax' => $programming->grade,
|
|
'grademin' => 0,
|
|
'gradetype' => GRADE_TYPE_VALUE);
|
|
$grades = new stdClass();
|
|
$grades->itemid = $cm->id;
|
|
$grades->userid = $submit->userid;
|
|
$grades->rawgrade = $grade;
|
|
|
|
return grade_update('mod/programming', $programming->course, 'mod', 'programming', $programming->id, 0, $grades, $params);
|
|
}
|
|
|
|
function programming_get_participants($programmingid) {
|
|
//Must return an array of user records (all data) who are participants
|
|
//for a given instance of programming. Must include every user involved
|
|
//in the instance, independient of his role (student, teacher, admin...)
|
|
//See other modules as example.
|
|
|
|
global $CFG, $DB;
|
|
$params = array($programmingid);
|
|
$sql = "SELECT DISTINCT u.*
|
|
FROM {programming_submits} AS ps,
|
|
{user} AS u,
|
|
WHERE ps.programmingid = ?
|
|
AND ps.userid = u.id";
|
|
$users = $DB->get_records_sql($sql, $params);
|
|
|
|
return $users;
|
|
}
|
|
|
|
function programming_scale_used($programmingid, $scaleid) {
|
|
//This function returns if a scale is being used by one programming
|
|
//it it has support for grading and scales. Commented code should be
|
|
//modified if necessary. See forum, glossary or journal modules
|
|
//as reference.
|
|
|
|
$return = false;
|
|
|
|
//$rec = get_record("programming","id","$programmingid","scale","-$scaleid");
|
|
//
|
|
//if (!empty($rec) && !empty($scaleid)) {
|
|
// $return = true;
|
|
//}
|
|
|
|
return $return;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
/// Any other programming functions go here. Each of them must have a name that
|
|
/// starts with programming_
|
|
|
|
function programming_get_showmode_options() {
|
|
$options = array();
|
|
$options['1'] = get_string('normalmode', 'programming');
|
|
$options['2'] = get_string('contestmode', 'programming');
|
|
return $options;
|
|
}
|
|
|
|
function programming_get_yesno_options() {
|
|
$yesnooptions = array();
|
|
$yesnooptions[0] = get_string('no');
|
|
$yesnooptions[1] = get_string('yes');
|
|
return $yesnooptions;
|
|
}
|
|
|
|
function programming_get_timelimit_options($default = 0) {
|
|
$timelimitoptions = array();
|
|
$timelimitoptions[0] = get_string('timelimitunlimited', 'programming');
|
|
for ($i = 1; $i <= 30; $i += 1) {
|
|
$timelimitoptions[$i] = get_string('nseconds', 'programming', $i);
|
|
}
|
|
return $timelimitoptions;
|
|
}
|
|
|
|
function programming_get_difficulty_options($default = 0) {
|
|
$difficultyoptions = array();
|
|
for ($i = 0; $i <= 10; $i += 1) {
|
|
$difficultyoptions[$i] = $i;
|
|
}
|
|
return $difficultyoptions;
|
|
}
|
|
|
|
function programming_testcase_pub_options() {
|
|
$options = array();
|
|
$options[PROGRAMMING_TEST_SHOWAFTERDISCOUNT] = get_string('afterdiscount', 'programming');
|
|
$options[PROGRAMMING_TEST_HIDDEN] = get_string('never', 'programming');
|
|
$options[PROGRAMMING_TEST_SHOWINRESULT] = get_string('inresult', 'programming');
|
|
$options[PROGRAMMING_TEST_SHOW] = get_string('always', 'programming');
|
|
return $options;
|
|
}
|
|
|
|
function programming_testcase_pub_getstring($pub) {
|
|
$options = programming_testcase_pub_options();
|
|
return $options[$pub];
|
|
}
|
|
|
|
function programming_testcase_require_view_capability($context, $case, $inresult = false, $afterdiscount = false) {
|
|
if ($case->pub == PROGRAMMING_TEST_HIDDEN ||
|
|
$case->pub == PROGRAMMING_TEST_SHOWINRESULT && $inresult ||
|
|
$case->pub == PROGRAMMING_TEST_SHOWAFTERDISCOUNT && $afterdiscount) {
|
|
require_capability('mod/programming:viewhiddentestcase', $context);
|
|
} else {
|
|
require_capability('mod/programming:viewpubtestcase', $context);
|
|
}
|
|
}
|
|
|
|
function programming_testcase_visible($tests, $result, $inresult = false, $afterdiscount = false) {
|
|
if (!is_object($result)) {
|
|
return false;
|
|
}
|
|
|
|
$pub = $tests[$result->testid]->pub;
|
|
return $pub == PROGRAMMING_TEST_SHOW ||
|
|
$pub == PROGRAMMING_TEST_SHOWINRESULT && $inresult ||
|
|
$pub == PROGRAMMING_TEST_SHOWAFTERDISCOUNT && $afterdiscount;
|
|
}
|
|
|
|
function programming_get_weight_options() {
|
|
$weightoptions = array();
|
|
//$weightoptions[0] = get_string('weightsetting', 'programming');
|
|
for ($i = 0; $i <= 10; $i += 1) {
|
|
$weightoptions[$i] = $i;
|
|
}
|
|
return $weightoptions;
|
|
}
|
|
|
|
function programming_get_memlimit_options() {
|
|
$memlimitoptions = array();
|
|
$memlimitoptions[0] = get_string('memlimitunlimited', 'programming');
|
|
for ($i = 256; $i <= 512; $i += 32) {
|
|
$memlimitoptions[$i] = $i . 'KB';
|
|
}
|
|
for ($i = 1; $i < 10; $i++) {
|
|
$memlimitoptions[$i * 1024] = $i . 'MB';
|
|
}
|
|
for ($i = 10; $i <= 550; $i+= 10) {
|
|
$memlimitoptions[$i * 1024] = $i . 'MB';
|
|
}
|
|
return $memlimitoptions;
|
|
}
|
|
|
|
function programming_get_nproc_options() {
|
|
$options = array();
|
|
for ($i = 0; $i <= 16; $i++) {
|
|
$options[$i] = "$i";
|
|
}
|
|
return $options;
|
|
}
|
|
|
|
function programming_get_language_options($programming = False) {
|
|
global $CFG, $DB;
|
|
if ($programming) {
|
|
$params = array($programming->id);
|
|
$sql = "SELECT * FROM {$CFG->prefix}programming_languages
|
|
WHERE id in (
|
|
SELECT languageid
|
|
FROM {programming_langlimit}
|
|
WHERE programmingid=?)";
|
|
$languages = $DB->get_records_sql($sql, $params);
|
|
if (!is_array($languages) || count($languages) == 0) {
|
|
$languages = $DB->get_records('programming_languages');
|
|
}
|
|
} else {
|
|
$languages = $DB->get_records('programming_languages');
|
|
}
|
|
$languageoptions = array();
|
|
foreach ($languages as $id => $row) {
|
|
// $languageoptions[$id] = $row->name;
|
|
$languageoptions[$id] = $row->description;
|
|
}
|
|
return $languageoptions;
|
|
}
|
|
|
|
function programming_get_category_options($programming = False) {
|
|
global $DB;
|
|
if ($programming) {
|
|
$params = array($programming->id);
|
|
$sql = "SELECT * FROM {programming_category}
|
|
WHERE catid in (
|
|
SELECT catid
|
|
FROM {programming_catproblemmap}
|
|
WHERE pid=?)";
|
|
$cats = $DB->get_records_sql($sql, $params);
|
|
if (!is_array($cats) || count($cats) == 0) {
|
|
$cats = $DB->get_records('programming_category');
|
|
}
|
|
} else {
|
|
$cats = $DB->get_records('programming_category');
|
|
}
|
|
$catoptions = array();
|
|
foreach ($cats as $id => $row) {
|
|
// $languageoptions[$id] = $row->name;
|
|
$catoptions[$id] = $row->catname;
|
|
}
|
|
return $catoptions;
|
|
}
|
|
|
|
function programming_get_language_options_js($programming = False) {
|
|
global $CFG, $DB;
|
|
if ($programming) {
|
|
$params = array($programming->id);
|
|
$sql = "SELECT * FROM {$CFG->prefix}programming_languages
|
|
WHERE id in (
|
|
SELECT languageid
|
|
FROM {programming_langlimit}
|
|
WHERE programmingid=?)";
|
|
$languages = $DB->get_records_sql($sql, $params);
|
|
if (!is_array($languages) || count($languages) == 0) {
|
|
$languages = $DB->get_records('programming_languages');
|
|
}
|
|
} else {
|
|
$languages = $DB->get_records('programming_languages');
|
|
}
|
|
$languageoptions = array();
|
|
foreach ($languages as $id => $row) {
|
|
// $languageoptions[$id] = $row->name;
|
|
$languageoptions[$id] = $row->langmode;
|
|
}
|
|
return $languageoptions;
|
|
}
|
|
|
|
function programming_test_add_instance($testcase) {
|
|
global $DB;
|
|
if (strlen($testcase->input) > 1024) {
|
|
$testcase->gzinput = bzcompress($testcase->input);
|
|
$testcase->input = mb_substr($testcase->input, 0, 1024, 'UTF-8');
|
|
}
|
|
if (strlen($testcase->output) > 1024) {
|
|
$testcase->gzoutput = bzcompress($testcase->output);
|
|
$testcase->output = mb_substr($testcase->output, 0, 1024, 'UTF-8');
|
|
}
|
|
$testcase->timemodified = time();
|
|
return $DB->insert_record("programming_tests", $testcase);
|
|
}
|
|
|
|
function programming_test_update_instance($testcase) {
|
|
# If input is not provided, do not update input and gzinput
|
|
global $DB;
|
|
if (empty($testcase->input)) {
|
|
unset($testcase->input);
|
|
unset($testcase->gzinput);
|
|
} else {
|
|
$testcase->gzinput = bzcompress($testcase->input);
|
|
$testcase->input = mb_substr($testcase->input, 0, 1024, 'UTF-8');
|
|
}
|
|
# If output is not provided, do not update output and gzoutput
|
|
if (empty($testcase->output)) {
|
|
unset($testcase->output);
|
|
unset($testcase->gzoutput);
|
|
} else {
|
|
$testcase->gzoutput = bzcompress($testcase->output);
|
|
$testcase->output = mb_substr($testcase->output, 0, 1024, 'UTF-8');
|
|
}
|
|
|
|
$testcase->timemodified = time();
|
|
return $DB->update_record('programming_tests', $testcase);
|
|
}
|
|
|
|
function programming_submit_add_instance($programming, $submit) {
|
|
global $CFG, $USER, $DB;
|
|
|
|
$submit->timemodified = time();
|
|
$submit->codelines = count(explode("\n", $submit->code));
|
|
$submit->codesize = strlen($submit->code);
|
|
$submit->status = 0;
|
|
$result = $DB->insert_record('programming_submits', $submit);
|
|
|
|
# update programming_result
|
|
if ($result) {
|
|
$r = $DB->get_record('programming_result', array('programmingid' => $programming->id, 'userid' => $USER->id));
|
|
if ($r) {
|
|
$r->submitcount++;
|
|
$r->latestsubmitid = $result;
|
|
$result2 = $DB->update_record('programming_result', $r);
|
|
} else {
|
|
$r = new stdClass;
|
|
$r->programmingid = $programming->id;
|
|
$r->userid = $submit->userid;
|
|
$r->submitcount = 1;
|
|
$r->latestsubmitid = $result;
|
|
$result2 = $DB->insert_record('programming_result', $r);
|
|
}
|
|
}
|
|
|
|
# update tester
|
|
if ($result) {
|
|
$DB->execute("INSERT INTO {programming_testers} (submitid, testerid) VALUES ($result, 0)");
|
|
}
|
|
|
|
# delete the old submits
|
|
if ($result and $programming->keeplatestonly) {
|
|
programming_delete_old_submits($programming->id, $submit->userid);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function programming_delete_old_submits($programmingid = -1, $userid = -1) {
|
|
global $CFG, $DB;
|
|
|
|
if ($programmingid > 0) {
|
|
if ($userid >= 0) {
|
|
$params = array($programmingid, $userid);
|
|
|
|
$sql = "SELECT id, programmingid, userid FROM
|
|
{programming_submits} WHERE
|
|
programmingid = ? AND userid = ? ORDER BY timemodified DESC";
|
|
} else {
|
|
$params = array($programmingid);
|
|
$sql = "SELECT id, programmingid, userid FROM
|
|
{programming_submits}
|
|
WHERE programmingid = ? ORDER BY userid, timemodified DESC";
|
|
}
|
|
} else {
|
|
$params = null;
|
|
$sql = "SELECT id, programmingid, userid FROM
|
|
{programming_submits}
|
|
ORDER BY programmingid, userid, timemodified DESC";
|
|
}
|
|
$submits = $DB->get_records_sql($sql, $params);
|
|
if (is_array($submits)) {
|
|
$ids = array();
|
|
$pprogramming = -1;
|
|
$puser = -1;
|
|
foreach ($submits as $row) {
|
|
if ($row->programmingid != $pprogramming) {
|
|
$puser = -1;
|
|
}
|
|
if ($row->userid != $puser) {
|
|
$c = 1;
|
|
} else {
|
|
$c++;
|
|
}
|
|
if ($c > PROGRAMMING_MAX_ATTEMPTS) {
|
|
$ids[] = $row->id;
|
|
}
|
|
$puser = $row->userid;
|
|
$pprogramming = $row->programmingid;
|
|
}
|
|
$ids = implode(',', $ids);
|
|
|
|
if ($ids) {
|
|
#delete from submits table
|
|
$DB->execute("DELETE FROM {programming_submits} WHERE id IN ({$ids})");
|
|
|
|
|
|
#delete from test results table
|
|
$DB->execute("DELETE FROM {programming_test_results} WHERE submitid IN ({$ids})");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function programming_get_submit_status_short($submit) {
|
|
switch ($submit->status) {
|
|
case PROGRAMMING_STATUS_NEW:
|
|
return get_string('statusshortnew', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILING:
|
|
return get_string('statusshortcompiling', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILEOK:
|
|
return get_string('statusshortcompileok', 'programming');
|
|
case PROGRAMMING_STATUS_RUNNING:
|
|
return get_string('statusshortrunning', 'programming');
|
|
case PROGRAMMING_STATUS_FINISH:
|
|
return get_string('statusshortfinish', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILEFAIL:
|
|
return get_string('statusshortcompilefail', 'programming');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function programming_get_submit_status_desc($submit) {
|
|
switch ($submit->status) {
|
|
case PROGRAMMING_STATUS_NEW:
|
|
return get_string('statusnew', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILING:
|
|
return get_string('statuscompiling', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILEOK:
|
|
return get_string('statuscompileok', 'programming');
|
|
case PROGRAMMING_STATUS_RUNNING:
|
|
return get_string('statusrunning', 'programming');
|
|
case PROGRAMMING_STATUS_FINISH:
|
|
return get_string('statusfinish', 'programming');
|
|
case PROGRAMMING_STATUS_COMPILEFAIL:
|
|
return get_string('statuscompilefail', 'programming');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function programming_get_test_results_short($submit) {
|
|
global $DB;
|
|
|
|
$c = new stdClass();
|
|
$c->total = $DB->count_records('programming_tests', array('programmingid' => $submit->programmingid));
|
|
$c->success = $DB->count_records('programming_test_results', array('submitid' => $submit->id, 'passed' => 1));
|
|
$c->fail = $c->total - $c->success;
|
|
if ($c->total == $c->success) {
|
|
return get_string('passalltests', 'programming', $c);
|
|
} else {
|
|
return get_string('successfailshort', 'programming', $c);
|
|
}
|
|
}
|
|
|
|
function programming_get_test_results_desc($submit, $results) {
|
|
$c = new stdClass();
|
|
$c->success = 0;
|
|
$c->fail = 0;
|
|
if (is_array($results)) {
|
|
foreach ($results as $result) {
|
|
if ($result->passed == 0)
|
|
$c->fail++;
|
|
else
|
|
$c->success++;
|
|
}
|
|
}
|
|
$c->total = $c->success + $c->fail;
|
|
return get_string('successfailcount', 'programming', $c);
|
|
}
|
|
|
|
function programming_format_codesize($size) {
|
|
if ($size < 1000)
|
|
return $size . 'B';
|
|
else
|
|
return round($size / 1000, 2) . 'K';
|
|
}
|
|
|
|
function check_crarr() {
|
|
global $_SERVER;
|
|
|
|
$uagent = $_SERVER['HTTP_USER_AGENT'];
|
|
if (!strpos($uagent, 'MSIE')) {
|
|
return '↵';
|
|
} else {
|
|
return '<span style="font-family: Wingdings 3">↵</span>';
|
|
}
|
|
}
|
|
|
|
function programming_format_io($message, $autolastreturn = false) {
|
|
$crarr = check_crarr();
|
|
$sizelimit = 1024;
|
|
|
|
$haslastreturn = false;
|
|
if (substr($message, strlen($message) - 1, 1) == "\n") {
|
|
$haslastreturn = true;
|
|
$message = substr($message, 0, strlen($message) - 1);
|
|
}
|
|
|
|
$message = str_replace(
|
|
array(' ', "\r", "\n"), array(' ', '', $crarr . '</span></li><li><span>'), htmlspecialchars(mb_substr($message, 0, $sizelimit, 'UTF-8')));
|
|
|
|
if ($haslastreturn || $autolastreturn)
|
|
$message .= $crarr;
|
|
|
|
return '<div><ol><li><span>' . $message . '</span></li></ol></div>';
|
|
}
|
|
|
|
function programming_format_compile_message($message) {
|
|
$lines = explode("\n", trim($message));
|
|
$html = '<ol>';
|
|
$t = '';
|
|
foreach ($lines as $line) {
|
|
if (strstr($line, 'warning:'))
|
|
$t = 'warning';
|
|
else if (strstr($line, 'error:'))
|
|
$t = 'error';
|
|
else
|
|
$t = 'normal';
|
|
|
|
$html .= "<li class='$t'>";
|
|
$html .= str_replace(' ', ' ', htmlspecialchars($line));
|
|
$html .= '</li>';
|
|
}
|
|
$html .= '</ol>';
|
|
return $html;
|
|
}
|
|
|
|
function programming_parse_compile_message($message) {
|
|
$r = new stdClass();
|
|
$r->warnings = count(preg_split("/\d: warning: /", $message)) - 1;
|
|
$r->errors = count(preg_split("/\d: error: /", $message)) - 1;
|
|
return $r;
|
|
}
|
|
|
|
function programming_delete_submit($submit) {
|
|
global $CFG, $DB;
|
|
|
|
$DB->delete_records('programming_test_results', array('submitid' => $submit->id));
|
|
$DB->delete_records('programming_submits', array('id' => $submit->id));
|
|
|
|
# update programming_result table
|
|
$params = array($submit->programmingid, $submit->userid);
|
|
$submits = $DB->get_records_sql("
|
|
SELECT * FROM {programming_submits}
|
|
WHERE programmingid = ?
|
|
AND userid = ?
|
|
ORDER BY timemodified DESC", $params);
|
|
$r = $DB->get_record('programming_result', array('programmingid' => $submit->programmingid, 'userid' => $submit->userid));
|
|
if (is_array($submits) && count($submits)) {
|
|
$s = array_shift($submits);
|
|
$r->latestsubmitid = $s->id;
|
|
} else {
|
|
$r->latestsubmitid = 0;
|
|
}
|
|
$DB->update_record('programming_result', $r);
|
|
}
|
|
|
|
function programming_rejudge($programming, $submitid, $groupid, $ac) {
|
|
global $CFG, $DB;
|
|
|
|
if (empty($submitid)) {
|
|
$params = array($programming->id, $programming->id);
|
|
$sql = "SELECT latestsubmitid
|
|
FROM {programming_result} AS pr,
|
|
{programming_submits} AS ps
|
|
WHERE pr.latestsubmitid = ps.id
|
|
AND pr.programmingid = ?
|
|
AND ps.programmingid = ?";
|
|
if (!$ac) {
|
|
$sql .= " AND (ps.passed = 0 OR ps.passed IS NULL)";
|
|
}
|
|
|
|
$lsids = $DB->get_records_sql($sql, $params);
|
|
foreach ($lsids as $submit) {
|
|
$ids[] = $submit->latestsubmitid;
|
|
}
|
|
} else {
|
|
$ids = $submitid;
|
|
}
|
|
$ids = implode(',', $ids);
|
|
|
|
$sql = "submitid IN ($ids)";
|
|
$DB->delete_records_select('programming_test_results', $sql);
|
|
|
|
$sql = "UPDATE {programming_submits}
|
|
SET judgeresult=null, timeused=null, memused=null,
|
|
passed=null, status=0, compilemessage=null
|
|
WHERE id IN ($ids)";
|
|
$DB->execute($sql);
|
|
|
|
$sql = "INSERT INTO {programming_testers}
|
|
SELECT id, 0, 3, 0
|
|
FROM {programming_submits}
|
|
WHERE id IN ($ids)
|
|
AND id NOT IN (SELECT submitid FROM {programming_testers})";
|
|
$DB->execute($sql);
|
|
}
|
|
|
|
function newme($params = NULL) {
|
|
$uri = me();
|
|
$append = '';
|
|
$search = array();
|
|
$replace = array();
|
|
if (is_array($params)) {
|
|
foreach ($params as $name => $value) {
|
|
$s = '/(.*)&' . $name . '=[\d\w+%]*(.*)/';
|
|
if (preg_match($s, $uri)) {
|
|
$search[] = $s;
|
|
$replace[] = '\1&' . $name . '=' . htmlspecialchars($value) . '\2';
|
|
} else {
|
|
$append .= '&' . $name . '=' . htmlspecialchars($value);
|
|
}
|
|
}
|
|
}
|
|
return preg_replace($search, $replace, $uri) . $append;
|
|
}
|
|
|
|
function programming_check_privilege($courseid, $groupid) {
|
|
global $USER;
|
|
if (!isteacher($courseid))
|
|
return False;
|
|
if (isteacheredit($courseid) || groupmode($courseid, $cm) == NOGROUPS)
|
|
return True;
|
|
if ($usergrps = groups_get_all_groups($courseid, $USER->id)) {
|
|
foreach ($usergrps as $ug) {
|
|
if ($groupid === $ug->id)
|
|
return True;
|
|
}
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
function programming_submit_timeused(&$results) {
|
|
$time = 0;
|
|
foreach ($results as $r) {
|
|
$time = max($time, $r->timeused);
|
|
}
|
|
return $time;
|
|
}
|
|
|
|
function programming_submit_memused(&$results) {
|
|
$mem = 0;
|
|
foreach ($results as $r) {
|
|
$mem = max($mem, $r->memused);
|
|
}
|
|
return $mem;
|
|
}
|
|
|
|
function programming_judgeresult_options($addempty = false) {
|
|
$rst = array('AC', 'PE', 'WA', 'RE', 'FPE', 'KS', 'OLE', 'MLE', 'TLE', 'RFC', 'JGE', 'JSE');
|
|
$ret = array();
|
|
if ($addempty)
|
|
$ret[''] = get_string('all');
|
|
foreach ($rst as $k) {
|
|
$ret[$k] = get_string($k, 'programming');
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
function programming_submit_judgeresult(&$results) {
|
|
$err = array('JSE' => 20, 'JGE' => 19, 'RFC' => 18,
|
|
'TLE' => 10, 'MLE' => 9, 'OLE' => 8,
|
|
'KS' => 14, 'FPE' => 13, 'RE' => 12, 'WA' => 11,
|
|
'PE' => 1, 'AC' => 0);
|
|
|
|
$c = -1;
|
|
$errstr = null;
|
|
foreach ($results as $result) {
|
|
if (isset($err[$result->judgeresult]) && $err[$result->judgeresult] > $c) {
|
|
$c = $err[$result->judgeresult];
|
|
$errstr = $result->judgeresult;
|
|
}
|
|
}
|
|
return $errstr;
|
|
}
|
|
|
|
function programming_contest_get_judgeresult(&$results) {
|
|
$errstr = programming_submit_judgeresult($results);
|
|
return get_string($errstr, 'programming');
|
|
}
|
|
|
|
function programming_get_judgeresult($result) {
|
|
if ($result->judgeresult) {
|
|
return '<a title="' . get_string($result->judgeresult . ':description', 'programming') . '">' . get_string($result->judgeresult, 'programming') . '</a>';
|
|
} else {
|
|
return programming_get_fail_reason($result);
|
|
}
|
|
}
|
|
|
|
function programming_get_fail_reason($result, $showmode = PROGRAMMING_SHOWMODE_NORMAL) {
|
|
if ($result->passed)
|
|
return get_string('n/a', 'programming');
|
|
|
|
switch ($result->signal) {
|
|
case 0:
|
|
break;
|
|
case 8:
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
|
|
return get_string('failbecausefpe', 'programming');
|
|
else
|
|
return get_string('runtimeerror', 'programming');
|
|
case 15:
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
|
|
return get_string('failbecausetimelimit', 'programming');
|
|
else
|
|
return get_string('timelimitexceed', 'programming');
|
|
case 11:
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
|
|
return get_string('failbecausesegv', 'programming');
|
|
else
|
|
return get_string('runtimeerror', 'programming');
|
|
case 9:
|
|
case 24:
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
|
|
return get_string('failbecausecputimelimit', 'programming');
|
|
else
|
|
return get_string('timelimitexceed', 'programming');
|
|
default:
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL)
|
|
return get_string('failbecauseunknownsig', 'programming', $result->signal);
|
|
else
|
|
return get_string('runtimeerror', 'programming');
|
|
}
|
|
|
|
if ($showmode == PROGRAMMING_SHOWMODE_NORMAL) {
|
|
switch ($result->exitcode) {
|
|
case 125:
|
|
return get_string('failbecausejudgescript', 'programming');
|
|
case 126:
|
|
return get_string('failbecauserestrict', 'programming');
|
|
case 127:
|
|
return get_string('failbecausesimpleguard', 'programming');
|
|
default:
|
|
return get_string('failbecausewrongresult', 'programming');
|
|
}
|
|
} else {
|
|
switch ($result->exitcode) {
|
|
case 125:
|
|
case 126:
|
|
case 127:
|
|
return get_string('runtimeerror', 'programming');
|
|
default:
|
|
return get_string('wronganswer', 'programming');
|
|
}
|
|
}
|
|
}
|
|
|
|
function programming_format_timelimit($timelimit) {
|
|
if ($timelimit) {
|
|
return get_string('nseconds', 'programming', $timelimit);
|
|
} else {
|
|
return get_string('timelimitunlimited', 'programming');
|
|
}
|
|
}
|
|
|
|
function programming_format_memlimit($memlimit) {
|
|
if ($memlimit) {
|
|
return get_string('nkb', 'programming', $memlimit);
|
|
} else {
|
|
return get_string('memlimitunlimited', 'programming');
|
|
}
|
|
}
|
|
|
|
function programming_judge_status($courseid, &$totalcount, $offset = 0, $limit = 15) {
|
|
global $DB;
|
|
|
|
if (!isset($courseid))
|
|
return False;
|
|
|
|
$crit = $courseid != 1 ? "p.course = $courseid AND" : '';
|
|
$sql = "SELECT ps.id AS psid,
|
|
ps.userid AS userid,
|
|
p.globalid,
|
|
p.name AS pname,
|
|
ps.timemodified as timemodified,
|
|
p.id AS pid,
|
|
ps.status as status
|
|
FROM {programming} AS p,
|
|
{programming_submits} AS ps
|
|
WHERE $crit p.id = ps.programmingid
|
|
ORDER BY ps.timemodified DESC";
|
|
$rows = $DB->get_records_sql($sql, null, $offset, $limit);
|
|
|
|
if ($courseid == 1) {
|
|
$sql = "SELECT COUNT(*) AS total
|
|
FROM {programming_submits} AS ps";
|
|
} else {
|
|
$sql = "SELECT COUNT(*) AS total
|
|
FROM {programming} AS p,
|
|
{programming_submits} AS ps
|
|
WHERE p.course = $courseid
|
|
AND ps.programmingid = p.id";
|
|
}
|
|
$totalcount = $DB->count_records_sql($sql);
|
|
|
|
if ($rows) {
|
|
foreach ($rows as $row) {
|
|
$row->user = $DB->get_record('user', array('id' => $row->userid));
|
|
switch ($row->status) {
|
|
case PROGRAMMING_STATUS_COMPILEFAIL:
|
|
$row->judgeresult = get_string('CE', 'programming');
|
|
break;
|
|
case PROGRAMMING_STATUS_FINISH:
|
|
$tr = $DB->get_records('programming_test_results', array('submitid' => $row->psid));
|
|
$row->judgeresult = '';
|
|
if ($tr) {
|
|
$row->judgeresult = programming_contest_get_judgeresult($tr);
|
|
$row->timeused = 0;
|
|
foreach ($tr as $r) {
|
|
$row->timeused = max($row->timeused, $r->timeused);
|
|
}
|
|
$row->memused = 'Unknown';
|
|
}
|
|
break;
|
|
default:
|
|
$row->judgeresult = get_string('statusshortnew', 'programming');
|
|
}
|
|
}
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
function programming_latest_ac($courseid, $roleid, &$totalcount, $offset = 0, $limit = 15) {
|
|
global $DB;
|
|
|
|
if (!isset($courseid))
|
|
return False;
|
|
$context = context_module::instance($courseid);
|
|
|
|
if ($courseid == 1) {
|
|
$sql = "SELECT ps.id AS psid,
|
|
ps.userid AS userid,
|
|
p.name AS pname,
|
|
p.globalid,
|
|
ps.timemodified as timemodified,
|
|
p.id AS pid
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps
|
|
WHERE pr.programmingid = p.id
|
|
AND pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1
|
|
ORDER BY ps.timemodified DESC";
|
|
$rows = $DB->get_records_sql($sql, null, $offset, $limit);
|
|
|
|
$sql = "SELECT COUNT(*) AS total
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps
|
|
WHERE pr.programmingid = p.id
|
|
AND pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1";
|
|
$totalcount = $DB->count_records_sql($sql);
|
|
} else {
|
|
$params = array($courseid, $roleid, $context->id);
|
|
|
|
$sql = "SELECT ps.id AS psid,
|
|
ps.userid AS userid,
|
|
p.name AS pname,
|
|
p.globalid,
|
|
ps.timemodified as timemodified,
|
|
p.id AS pid
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps,
|
|
{role_assignments} AS ra
|
|
WHERE p.course = ?
|
|
AND ra.roleid = ?
|
|
AND ra.contextid = ?
|
|
AND pr.programmingid = p.id
|
|
AND pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1
|
|
AND pr.userid = ra.userid
|
|
ORDER BY ps.timemodified DESC";
|
|
$rows = $DB->get_records_sql($sql, $params, $offset, $limit);
|
|
$params = array($courseid, $roleid, $context->id);
|
|
|
|
$sql = "SELECT COUNT(*) AS total
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps,
|
|
{role_assignments} AS ra
|
|
WHERE p.course = ?
|
|
AND ra.roleid = ?
|
|
AND ra.contextid = ?
|
|
AND pr.programmingid = p.id
|
|
AND pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1
|
|
AND pr.userid = ra.userid";
|
|
$totalcount = $DB->count_records_sql($sql, $params);
|
|
}
|
|
|
|
if ($rows) {
|
|
foreach ($rows as $row) {
|
|
$row->user = $DB->get_record('user', array('id' => $row->userid));
|
|
}
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* Calculate the standing of the contest.
|
|
*
|
|
* 1. Each run is either accepted or rejected.
|
|
* 2. The problem is considered solved by the team, if one of the runs
|
|
* submitted for it is accepted.
|
|
* 3. The time consumed for a solved problem is the time elapsed from
|
|
* the beginning of the contest to the submission of the first accepted
|
|
* run for this problem (in minutes) plus 20 minutes for every other run
|
|
* for this problem before the accepted one. For an unsolved problem
|
|
* consumed time is not computed.
|
|
* 4. The total time is the sum of the time consumed for each problem solved.
|
|
* 5. Teams are ranked according to the number of solved problems. Teams that
|
|
* solve the same number of problems are ranked by the least total time.
|
|
* 6. While the time shown is in minutes, the actual time is measured to the
|
|
* precision of 1 second, and the the seconds are taken into account when
|
|
* ranking teams.
|
|
* 7. Teams with equal rank according to the above rules must be sorted by
|
|
* increasing team number.
|
|
*
|
|
* @param courseid In which course the contest is hold.
|
|
* @param wrongsubmitminutes How many minutes for wrong submit.
|
|
* @param roleid Which role is included
|
|
* @return An array contains all the information of teams.
|
|
*/
|
|
function programming_calc_standing($courseid, $roleid, $wrongsubmitminutes = 20, $offset = 0, $limit = 10) {
|
|
global $DB;
|
|
|
|
if (!isset($courseid))
|
|
return array();
|
|
if ($courseid == 1) {
|
|
$query = "SELECT pr.userid,
|
|
COUNT(*) AS ac
|
|
FROM {programming_result} AS pr,
|
|
{programming_submits} AS ps
|
|
WHERE pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1
|
|
GROUP BY pr.userid
|
|
ORDER BY ac DESC";
|
|
} else {
|
|
$context = context_course::instance($courseid);
|
|
$wss = $wrongsubmitminutes * 60;
|
|
|
|
$query = "SELECT pr.userid,
|
|
SUM(ps.passed) AS ac,
|
|
SUM((ps.timemodified-p.timeopen)*ps.passed) AS timeused,
|
|
SUM(pr.submitcount * ps.passed) AS submitcount,
|
|
SUM(((ps.timemodified - p.timeopen) +
|
|
(pr.submitcount - ps.passed)*{$wss}) * ps.passed) AS penalty
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps,
|
|
{role_assignments} AS ra
|
|
WHERE p.course={$courseid}
|
|
AND ra.roleid = {$roleid}
|
|
AND ra.contextid = {$context->id}
|
|
AND ps.programmingid = p.id
|
|
AND pr.programmingid = p.id
|
|
AND pr.userid = ra.userid
|
|
AND pr.latestsubmitid = ps.id
|
|
GROUP BY pr.userid
|
|
HAVING ac > 0
|
|
ORDER BY ac DESC, penalty ASC";
|
|
}
|
|
|
|
//echo $query;
|
|
|
|
$standing = $DB->get_records_sql($query, null, $offset, $limit);
|
|
if (!is_array($standing))
|
|
$standing = array();
|
|
foreach ($standing as $row) {
|
|
$row->user = $DB->get_record('user', array('id' => $row->userid));
|
|
}
|
|
|
|
return $standing;
|
|
}
|
|
|
|
function programming_count_standing($courseid, $roleid) {
|
|
global $DB;
|
|
|
|
if (!isset($courseid))
|
|
return 0;
|
|
if ($courseid == 1) {
|
|
$query = "SELECT COUNT(*) AS COUNT FROM (
|
|
SELECT DISTINCT pr.userid
|
|
FROM {programming_result} AS pr,
|
|
{programming_submits} AS ps
|
|
WHERE pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1) AS c";
|
|
} else {
|
|
$context = context_course::instance($courseid);
|
|
|
|
$query = "SELECT COUNT(*) AS count FROM (
|
|
SELECT DISTINCT pr.userid
|
|
FROM {programming} AS p,
|
|
{programming_result} AS pr,
|
|
{programming_submits} AS ps,
|
|
{role_assignments} AS ra
|
|
WHERE p.course={$courseid}
|
|
AND ra.roleid = {$roleid}
|
|
AND ra.contextid = {$context->id}
|
|
AND ps.programmingid = p.id
|
|
AND pr.programmingid = p.id
|
|
AND pr.userid = ra.userid
|
|
AND pr.latestsubmitid = ps.id
|
|
AND ps.passed = 1) AS c";
|
|
}
|
|
//echo $query;
|
|
|
|
return $DB->count_records_sql($query);
|
|
}
|
|
|
|
function programming_submit_remove_preset($code) {
|
|
$ret = array();
|
|
$s = 0;
|
|
|
|
$lines = explode("\n", $code);
|
|
foreach ($lines as $line) {
|
|
$line = trim($line, "\r");
|
|
switch ($s) {
|
|
case 0:
|
|
if ($line == PROGRAMMING_PRESET_BEGIN) {
|
|
$s = 1;
|
|
}
|
|
if ($s == 0) {
|
|
$ret[] = $line;
|
|
}
|
|
break;
|
|
case 1:
|
|
if ($line == PROGRAMMING_PRESET_END) {
|
|
$s = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return implode("\n", $ret);
|
|
}
|
|
|
|
/**
|
|
* Put prepend and postpend preset code and user submitted code together.
|
|
*/
|
|
function programming_format_code($programming, $submit, $check = false) {
|
|
global $DB;
|
|
if (is_object($programming)) {
|
|
$programming = $programming->id;
|
|
}
|
|
$prepend = $DB->get_record('programming_presetcode', array('programmingid' => $programming, 'languageid' => $submit->language, 'name' => '<prepend>'));
|
|
$postpend = $DB->get_record('programming_presetcode', array('programmingid' => $programming, 'languageid' => $submit->language, 'name' => '<postpend>'));
|
|
|
|
$ret = array();
|
|
if (!empty($prepend)) {
|
|
$ret[] = PROGRAMMING_PRESET_BEGIN;
|
|
$ret[] = trim(!$check || empty($prepend->presetcodeforcheck) ? $prepend->presetcode : $prepend->presetcodeforcheck);
|
|
$ret[] = PROGRAMMING_PRESET_END;
|
|
}
|
|
|
|
if (is_object($submit)) {
|
|
$ret[] = $submit->code;
|
|
}
|
|
|
|
if (!empty($postpend)) {
|
|
$ret[] = PROGRAMMING_PRESET_BEGIN;
|
|
$ret[] = trim(!$check || empty($postpend->presetcodeforcheck) ? $postpend->presetcode : $postpend->presetcodeforcheck);
|
|
$ret[] = PROGRAMMING_PRESET_END;
|
|
}
|
|
return implode("\n\n", $ret);
|
|
}
|
|
|
|
function programming_get_presetcode_name($presetcode) {
|
|
switch ($presetcode->name) {
|
|
case '<prepend>':
|
|
return get_string('prependcode', 'programming');
|
|
case '<postpend>':
|
|
return get_string('postpendcode', 'programming');
|
|
default:
|
|
return $presetcode->name;
|
|
}
|
|
}
|
|
|
|
function programming_format_presetcode($presetcode) {
|
|
$ret = array(PROGRAMMING_PRESET_BEGIN, trim($presetcode->presetcode),
|
|
PROGRAMMING_PRESET_END);
|
|
return implode("\n\n", $ret);
|
|
}
|
|
|
|
function get_visible_programmings($courseid) {
|
|
$DB;
|
|
$sql = "SELECT p.*, cm.visible
|
|
FROM {course_modules} cm,
|
|
{programming} p
|
|
WHERE cm.course=$courseid
|
|
AND p.course=$courseid
|
|
AND cm.instance = p.id
|
|
ORDER BY p.name";
|
|
return $DB->get_records_sql($sql);
|
|
}
|
|
|
|
function programming_adjust_sequence(&$records, $moveid, $direction) {
|
|
$seq = array();
|
|
$i = 1;
|
|
$idx = 0;
|
|
foreach ($records as $rec) {
|
|
if ($moveid == $rec->id)
|
|
$idx = $i;
|
|
$seq[$i++] = $rec;
|
|
}
|
|
|
|
if ($idx) {
|
|
if ($direction == 1 && $idx > 1) { // move up
|
|
$t = $seq[$idx];
|
|
$seq[$idx] = $seq[$idx - 1];
|
|
$seq[$idx - 1] = $t;
|
|
} else if ($direction == 2 && $idx < count($records)) {
|
|
$t = $seq[$idx];
|
|
$seq[$idx] = $seq[$idx + 1];
|
|
$seq[$idx + 1] = $t;
|
|
}
|
|
}
|
|
return $seq;
|
|
}
|
|
|
|
function programming_presetcode_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
|
|
global $DB;
|
|
$codes = $DB->get_records('programming_presetcode', array('programmingid' => $programmingid, 'sequence' => 'id, sequence'));
|
|
if (!is_array($codes))
|
|
return;
|
|
|
|
$seq = programming_adjust_sequence($codes, $moveid, $direction);
|
|
|
|
foreach ($seq as $i => $code) {
|
|
if ($code->sequence != $i) {
|
|
$code->sequence = $i;
|
|
$DB->update_record('programming_presetcode', $code);
|
|
}
|
|
}
|
|
}
|
|
|
|
function programming_datafile_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
|
|
global $DB;
|
|
$codes = $DB->get_records('programming_datafile', array('programmingid' => $programmingid, 'seq' => 'id, seq'));
|
|
if (!is_array($codes))
|
|
return;
|
|
|
|
$seq = programming_adjust_sequence($codes, $moveid, $direction);
|
|
|
|
foreach ($seq as $i => $code) {
|
|
if ($code->seq != $i) {
|
|
$code->seq = $i;
|
|
$DB->update_record('programming_datafile', $code);
|
|
}
|
|
}
|
|
}
|
|
|
|
function programming_testcase_adjust_sequence($programmingid, $moveid = 0, $direction = 0) {
|
|
global $DB;
|
|
$codes = $DB->get_records('programming_tests', array('programmingid' => $programmingid, 'seq' => 'id, seq'));
|
|
if (!is_array($codes))
|
|
return;
|
|
|
|
$seq = programming_adjust_sequence($codes, $moveid, $direction);
|
|
|
|
foreach ($seq as $i => $code) {
|
|
if ($code->seq != $i) {
|
|
$code->seq = $i;
|
|
$DB->update_record('programming_tests', $code);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $feature FEATURE_xx constant for requested feature
|
|
* @return mixed True if module supports feature, null if doesn't know
|
|
*/
|
|
function programming_supports($feature) {
|
|
switch ($feature) {
|
|
case FEATURE_GROUPS: return true;
|
|
case FEATURE_GROUPINGS: return true;
|
|
case FEATURE_GROUPMEMBERSONLY: return true;
|
|
case FEATURE_MOD_INTRO: return true;
|
|
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
|
|
case FEATURE_GRADE_HAS_GRADE: return true;
|
|
case FEATURE_GRADE_OUTCOMES: return true;
|
|
case FEATURE_BACKUP_MOODLE2: return true;
|
|
case FEATURE_SHOW_DESCRIPTION: return true;
|
|
case FEATURE_ADVANCED_GRADING: return false;
|
|
|
|
default: return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This fucntion extends the global navigation for the site.
|
|
* It is important to note that you should not rely on PAGE objects within this
|
|
* body of code as there is no guarantee that during an AJAX request they are
|
|
* available
|
|
*
|
|
* @param navigation_node $navigation The programming node within the global navigation
|
|
* @param object $course The course object returned from the DB
|
|
* @param object $module The module object returned from the DB
|
|
* @param object $cm The course module instance returned from the DB
|
|
*/
|
|
function programming_extend_navigation($navigation, $course, $module, $cm) {
|
|
global $CFG, $USER, $PAGE, $OUTPUT;
|
|
|
|
$currentgroup = groups_get_activity_group($cm, true);
|
|
|
|
$target = $CFG->wwwroot . '/mod/programming/';
|
|
$param = array('id' => $cm->id);
|
|
|
|
$navigation->add(get_string('view', 'programming'), new moodle_url($target . 'view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$navigation->add(get_string('submit', 'programming'), new moodle_url($target . 'submit.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$navigation->add(get_string('result', 'programming'), new moodle_url($target . 'result.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$navigation->add(get_string('submithistory', 'programming'), new moodle_url($target . 'history.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
|
|
$context = context_module::instance($cm->id);
|
|
|
|
if (has_capability('mod/programming:viewreport', $context)) {
|
|
$reportnode = $navigation->add(get_string('reports', 'programming'), new moodle_url($target . 'reports/summary.php', $param), navigation_node::TYPE_CONTAINER, null, null, new pix_icon('c/group', ''));
|
|
$reportnode->add(get_string('summary', 'programming'), new moodle_url($target . 'reports/summary.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$reportnode->add(get_string('detail', 'programming'), new moodle_url($target . 'reports/detail.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$reportnode->add(get_string('bestprograms', 'programming'), new moodle_url($target . 'reports/best.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
$reportnode->add(get_string('judgeresultcountchart', 'programming'), new moodle_url($target . 'reports/judgeresultchart.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
}
|
|
|
|
if (has_capability('mod/programming:viewresemble', $context)) {
|
|
$navigation->add(get_string('resemble', 'programming'), new moodle_url($target . 'resemble/view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
}
|
|
}
|
|
|
|
function programming_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $navigation) {
|
|
global $USER, $PAGE, $CFG, $DB, $OUTPUT;
|
|
|
|
$target = $CFG->wwwroot . '/mod/programming/';
|
|
$param = array('id' => $PAGE->cm->id);
|
|
|
|
$navigation->add(get_string('view', 'programming'), new moodle_url($target . 'view.php', $param), navigation_node::TYPE_USER, null, null, new pix_icon('c/group', ''));
|
|
}
|
|
|
|
function programming_navtab($currenttab, $currenttab2, $course, $module, $cm) {
|
|
global $CFG;
|
|
$context = context_module::instance($cm->id);
|
|
$tabs = array();
|
|
$inactive = NULL;
|
|
|
|
$row = array();
|
|
|
|
$row[] = new tabobject('view', $CFG->wwwroot . '/mod/programming/view.php?id=' . $cm->id, get_string('view', 'programming'), '', true);
|
|
|
|
$row[] = new tabobject('submit', $CFG->wwwroot . '/mod/programming/submit.php?id=' . $cm->id, get_string('submit', 'programming'), '', true);
|
|
|
|
$row[] = new tabobject('result', $CFG->wwwroot . '/mod/programming/result.php?id=' . $cm->id, get_string('result', 'programming'), '', true);
|
|
|
|
$row[] = new tabobject('history', $CFG->wwwroot . '/mod/programming/history.php?id=' . $cm->id, get_string('submithistory', 'programming'), '', true);
|
|
|
|
if (has_capability('mod/programming:edittestcase', $context)) {
|
|
$row[] = new tabobject('edittest', $CFG->wwwroot . '/mod/programming/testcase/list.php?id=' . $cm->id, get_string('testenv', 'programming'));
|
|
}
|
|
if (has_capability('mod/programming:viewreport', $context)) {
|
|
$row[] = new tabobject('reports', $CFG->wwwroot . '/mod/programming/reports/summary.php?id=' . $cm->id, get_string('reports', 'programming'), '', true);
|
|
}
|
|
if (has_capability('mod/programming:viewresemble', $context)) {
|
|
$row[] = new tabobject('resemble', $CFG->wwwroot . '/mod/programming/resemble/view.php?id=' . $cm->id, get_string('resemble', 'programming'), '', true);
|
|
}
|
|
|
|
$tabs[] = $row;
|
|
|
|
if ($currenttab == 'edittest' && has_capability('mod/programming:edittestcase', $context)) {
|
|
$row = array();
|
|
$inactive[] = 'edittest';
|
|
$row[] = new tabobject('testcase', $CFG->wwwroot . '/mod/programming/testcase/list.php?id=' . $cm->id, get_string('testcase', 'programming'));
|
|
$row[] = new tabobject('datafile', $CFG->wwwroot . '/mod/programming/datafile/list.php?id=' . $cm->id, get_string('datafile', 'programming'));
|
|
$row[] = new tabobject('presetcode', $CFG->wwwroot . '/mod/programming/presetcode/list.php?id=' . $cm->id, get_string('presetcode', 'programming'));
|
|
$row[] = new tabobject('validator', $CFG->wwwroot . '/mod/programming/validator/edit.php?id=' . $cm->id, get_string('validator', 'programming'));
|
|
|
|
$tabs[] = $row;
|
|
}
|
|
|
|
if ($currenttab == 'reports') {
|
|
$row = array();
|
|
$inactive[] = 'reports';
|
|
$row[] = new tabobject('reports-summary', $CFG->wwwroot . '/mod/programming/reports/summary.php?id=' . $cm->id, get_string('summary', 'programming'));
|
|
$row[] = new tabobject('reports-detail', $CFG->wwwroot . '/mod/programming/reports/detail.php?id=' . $cm->id . '&latestonly=1', get_string('detail', 'programming'));
|
|
$row[] = new tabobject('reports-best', $CFG->wwwroot . '/mod/programming/reports/best.php?id=' . $cm->id, get_string('bestprograms', 'programming'));
|
|
$row[] = new tabobject('reports-judgeresultchart', $CFG->wwwroot . '/mod/programming/reports/judgeresultchart.php?id=' . $cm->id, get_string('judgeresultcountchart', 'programming'));
|
|
$tabs[] = $row;
|
|
}
|
|
|
|
if ($currenttab == 'resemble') {
|
|
$row = array();
|
|
$inactive[] = 'resemble';
|
|
$row[] = new tabobject('resemble-view', $CFG->wwwroot . '/mod/programming/resemble/view.php?id=' . $cm->id, get_string('personal', 'programming'));
|
|
if (has_capability('mod/programming:editresemble', $context)) {
|
|
$row[] = new tabobject('resemble-edit', $CFG->wwwroot . '/mod/programming/resemble/edit.php?id=' . $cm->id, get_string('edit'));
|
|
}
|
|
if (has_capability('mod/programming:updateresemble', $context)) {
|
|
$row[] = new tabobject('resemble-analyze', $CFG->wwwroot . '/mod/programming/resemble/analyze.php?id=' . $cm->id, get_string('analyze', 'programming'));
|
|
}
|
|
$tabs[] = $row;
|
|
}
|
|
|
|
/* * ***************************
|
|
* stolen code from quiz report
|
|
* *************************** */
|
|
if ($currenttab == 'templates' and isset($mode)) {
|
|
$inactive[] = 'templates';
|
|
$templatelist = array('listtemplate', 'singletemplate', 'addtemplate', 'rsstemplate', 'csstemplate');
|
|
|
|
$row = array();
|
|
$currenttab = '';
|
|
foreach ($templatelist as $template) {
|
|
$row[] = new tabobject($template, "templates.php?d=$data->id&mode=$template", get_string($template, 'data'));
|
|
if ($template == $mode) {
|
|
$currenttab = $template;
|
|
}
|
|
}
|
|
$tabs[] = $row;
|
|
}
|
|
|
|
$tab = new StdClass;
|
|
$tab->tabs = $tabs;
|
|
$tab->currenttab = $currenttab;
|
|
$tab->active = empty($currenttab2) ? null : array($currenttab2);
|
|
$tab->inactive = $inactive;
|
|
|
|
return $tab;
|
|
}
|
|
|
|
?>
|