Skip to main content

Overview

Submissions are student code submissions that get automatically judged against test cases. Each submission is tracked, graded, and can be selected as final.

Submission Model

The Submission model tracks all submission data:
Submission.php:26-27
protected $fillable = [
    'id', 'user_id','assignment_id','problem_id','is_final',
    'time','status','pre_score','coefficient','file_name',
    'language_id', 'judgement'
];

Key Fields

Identification

  • user_id: Who submitted
  • assignment_id: Assignment (0 for practice)
  • problem_id: Which problem
  • language_id: Programming language used

Grading

  • pre_score: Raw score (0-10000, where 10000 = 100%)
  • coefficient: Late penalty multiplier (0-100)
  • status: PENDING, SCORE, ERROR, etc.
  • judgement: Detailed test case results (JSON)

Selection

  • is_final: Whether this is the final submission
  • file_name: Stored filename
  • time: Execution time

Submission Process

Creating a Submission

Route: GET /submissions/create/assignment/{assignment}/problem/{problem}
submission_controller.php:131-152
public function create($assignment_id, $problem_id, $old_sub = -1){
    $assignment = Assignment::find($assignment_id);
    $problem = $this->_creation_guard_check($assignment_id, $problem_id);

    // Load last submission for this user/problem
    $last = Submission::where([
        'assignment_id' => $assignment_id, 
        'problem_id' => $problem_id, 
        'user_id' => Auth::user()->id
    ]);
    
    if ($old_sub != -1) $last = $last->where(['id'=> $old_sub]);
    $last = $last->get()->last();

    $last_code = null;
    if ($last != null){
        $submit_path = Submission::get_path($last->user->username, $last->assignment_id, $last->problem_id);
        $file_extension = $last->language->extension;
        $file_path = $submit_path . "/{$last->file_name}.". $file_extension;
        $last_code = file_exists($file_path)? file_get_contents($file_path): null;
    }

    return view('submissions.create', ['assignment' => $assignment, 'problem' => $problem, 'last_code' => $last_code]);
}

Submission Guards

submission_controller.php:111-129
private function _creation_guard_check($assignment_id, $problem_id, $language_id = -1){
    $assignment = Assignment::find($assignment_id);
    $problem = Problem::find($problem_id);

    if ( $assignment_id != 0 && $assignment->problems->find($problem_id) == NULL) 
        abort(404);

    $check = $assignment->can_submit(Auth::user(), $problem);
    if (!$check->can_submit){
        abort(403, $check->error_message);
    }

    if ($language_id != -1 && !in_array($language_id, explode(", ", $assignment->language_ids))){
        abort(403, "This assignment doesn't allow programming language of id " . $language_id) ;
    }

    return $problem;
}

File Upload

Submission Storage

Route: POST /submissions/store
submission_controller.php:170-182
public function upload_file_code($request, $user_dir, $submission)
{
    if ($request->userfile->getSize() > Setting::get("file_size_limit") * 1024 )
        abort(403, "Your submission is larger than system limited size");

    // Use assignments submit count in the name
    $file_name = "solution-upload-count".($submission->assignment->total_submits);

    $path = $request->userfile->storeAs(
        $user_dir, 
        $file_name.".".$submission->language->extension, 
        'assignment_root'
    );

    $this->add_to_queue($submission, $submission->assignment, $file_name);
    return TRUE;
}

File Organization

Submission.php:51-59
public static function get_path($username, $assignment_id, $problem_id)
{
    $assignment_root = rtrim(Setting::get("assignments_root"),'/');
    return $assignment_root . "/assignment_{$assignment_id}/problem_{$problem_id}/{$username}";
}

public static function get_relative_path($username, $assignment_id, $problem_id)
{
    return  "/assignment_{$assignment_id}/problem_{$problem_id}/{$username}";
}

Language Selection

Each problem supports specific programming languages with custom limits:
submission_controller.php:348-350
if ($problem->languages->where('id',$language->id)->count() == 0)
    abort(403,'This file type is not allowed for this problem.');

Code Templates

Route: POST /submissions/get_template

Template Structure

Templates provide:
  • Banned keywords: Prevent use of certain functions
  • Before code: Pre-written setup code
  • After code: Post-insertion code
  • Full template: Complete starter code
submission_controller.php:287-323
public function get_template(Request $request){
    $validated = $request->validate([
        'assignment_id' => ['integer'],
        'problem_id' => 'integer',
        'language_id' => 'integer'
    ]);

    $problem = $this->_creation_guard_check(
        $request->input('assignment_id'), 
        $request->input('problem_id')
    );

    $template = $problem->template_content($request->input('language_id'));

    if ($template === NULL){
        $result = array('banned' => '', 'before'  => '', 'after' => '', 'full' => '');
    } else {
        preg_match("/(\/*###Begin banned.*\n)((.*\n)*)(###End banned keyword\*\/)/", 
            $template, $matches
        );
        $banned = isset($matches[2]) ? $matches[2] : "";

        preg_match("/(###End banned keyword\*\/\n)((.*\n)*)\/\/###INSERT CODE HERE -\n?((.*\n?)*)/", 
            $template, $matches
        );
        
        $before = isset($matches[2]) ? $matches[2] : "";
        $after = isset($matches[4]) ? $matches[4] : "";

        $result = array(
            'banned' => $banned, 
            'before'  => $before, 
            'after' => $after, 
            'full' => $template
        );
    }

    return response()->json($result);
}

Viewing Results

View Submission Code

Route: POST /submissions/view_code
submission_controller.php:398-435
public function view_code()
{
    $submit_id = $_POST['submit_id'];
    $type = $_POST['type'];

    $submission = Submission::with('assignment')->find($submit_id);

    if (!$submission) abort(403,"Submission not found");
    $this->_do_access_check($submission);

    $submit_path = Submission::get_relative_path(
        $submission->user->username, 
        $submission->assignment_id, 
        $submission->problem_id
    );
    $file_extension = $submission->language->extension;

    if ($type == "code")
        $file_path = $submit_path . "/{$submission->file_name}.". $file_extension;
    elseif ($type == "log")
        $file_path = $submit_path . "/log-{$submission->id}";
    elseif ($type == "result")
        $file_path = $submit_path . "/result-{$submission->id}.html";

    $file_content = $this->storage->exists($file_path) 
        ? $this->storage->get($file_path) 
        : "File not found";

    $result = array(
        'file_name' => $submission->file_name .'.'. $file_extension,
        'text' => $file_content
    );
    
    if ($type === 'code') {
        $result['lang'] = $file_extension;
        if (in_array($result['lang'],  ['py2', 'py3']))
            $result['lang'] = 'python';
        else if ($result['lang'] == 'pas')
            $result['lang'] = 'pascal';
    }

    return $result;
}

View Submission Status

Route: POST /submissions/view_status
submission_controller.php:451-467
public function view_status(){
    $submit_id = $_POST['submit_id'];

    $submission = Submission::with('assignment')->find($submit_id);

    if (!$submission) abort(403,"Submission not found");
    $this->_do_access_check($submission);

    $this->_status($submission);

    $a = new verdict($submission);
    $submission->rendered_verdict = $a->resolveView()->with($a->data())->render();
    
    echo json_encode($submission);
}

Access Control

submission_controller.php:36-43
private function _do_access_check($submission){
    if (in_array(Auth::user()->role->name, ['student', 'guest'])){
        if ($submission->user_id!=Auth::user()->id)
            abort(403,"You don't have permission to view another user's submissions");
        if (!$submission->assignment->open)
            abort(403,"This assignment has been close for students");
    }
}

Final Submission Selection

Selecting Final Submission

Route: POST /submissions/select Students can choose which submission counts for their grade:
submission_controller.php:382-397
public function select_final(Request $request)
{
    $submission_curr = Submission::find($request->submission);
    if (!$submission_curr) abort(403,"Submission not found");
    $this->_do_access_check($submission_curr);

    // Unmark previous final submission
    $submission_final = Submission::where(array(
        'user_id' => $submission_curr->user_id, 
        'assignment_id' => $submission_curr->assignment_id, 
        'problem_id' => $submission_curr->problem_id, 
        'is_final' => 1
    ))->update(['is_final' => 0]);

    // Mark new final submission
    $submission_curr->is_final = 1;
    $submission_curr->save();

    Scoreboard::update_scoreboard( $submission_curr->assignment_id );
    return response()->json(['done' => 1]);
}

Auto-select Best Submission

Assignments can automatically reset final submissions to best scores:
Assignment.php:252-314
public function reset_final_submission_choices()
{
    $problem_score = $this->problems->pluck('pivot.score', 'id');
    $subs = $this->submissions()->oldest()->get()->keyBy('id');

    $final_subs = [];
    foreach ($subs as $sub) {
        $key = $sub->user_id . "," . $sub->problem_id;
        $sub->is_final = 0;
        $change = true;
        
        if (isset($final_subs[$key])) {
            $final = $subs[$final_subs[$key]];

            // Calculate final scores
            $final_score = ceil(
                ($final->pre_score * ($problem_score[$final->problem_id] ?? 0)) / 10000
            );
            $final_score = ceil(
                $final_score * ($final->coefficient === "error" ? 0 : $final->coefficient / 100)
            );

            $sub_score = ceil(
                ($sub->pre_score * ($problem_score[$sub->problem_id] ?? 0)) / 10000
            );
            $sub_score = ceil(
                $sub_score * ($sub->coefficient === "error" ? 0 : $sub->coefficient / 100)
            );

            // Prefer 100% solutions, then higher scores
            if ($sub->pre_score == 10000) {
                if ($final->pre_score == 10000 && $sub_score <= $final_score) {
                    $change = false;
                }
            } else {
                if ($final->pre_score == 10000) {
                    $change = false;
                } elseif ($sub_score <= $final_score) {
                    $change = false;
                }
            }
            
            if ($change) {
                $final->is_final = 0;
                $final->save();
            }
        }
        
        if ($change) {
            $final_subs[$key] = $sub->id;
            $sub->is_final = 1;
        }
        $sub->save();
    }
}

Rejudging Submissions

Rejudge Single Submission

Route: POST /submissions/rejudge
submission_controller.php:263-285
public function rejudge(Request $request){
    if (!in_array( Auth::user()->role->name, ['student', 'guest'])){
        $validated = $request->validate([
            'submission_id' => ['integer'],
        ]);

        $sub = Submission::find($request->submission_id);
        if ($sub == NULL) abort(404);

        if (Queue_item::where('submission_id', $sub->id)->count() > 0){
            return response()->json([
                'done' => 0, 
                'message' => 'Submission is already in queue for judging'
            ]);
        }

        $a = Queue_item::add_and_process($sub->id, 'rejudge');
        $sub->status = 'PENDING';
        $sub->save();

        return response()->json(['done' => 1]);
    }
}

Rejudge All Submissions

Route: POST /submissions/rejudge_all_problems_assignment
submission_controller.php:232-261
public function rejudge_all_problems_assignment(Request $request)
{
    if (!in_array( Auth::user()->role->name, ['admin', 'head_instructor', 'instructor']))
        abort(403,"You don't have permission to do that");
        
    if ($request->assignment_id != null)
        $assignment = Assignment::with('problems')->find($request->assignment_id);
    else {
        abort(404, 'Need assignment to do rejudge all');
    }

    if ($request->problem_id == 'all')
        $submissions = Submission::where('assignment_id',$assignment->id)->get();
    else
        $submissions = Submission::where('assignment_id',$assignment->id)
            ->where('problem_id', $request->problem_id)->get();

    if ($submissions->count() == 0){
        abort(404, 'invalid assignment_id and problem_id combo');
    }
    
    foreach ($submissions as $submission)
    {
        $a = Queue_item::add_not_process($submission->id, 'rejudge');
        $submission->status = 'PENDING';
        $submission->save();
    }
    
    for ($i=0; $i < Setting::get('concurent_queue_process', 2); $i++) {
        Queue_item::work();
    }
    
    return redirect()->back()->with('status', 'Rejudge in progress');
}

Submission Listing

Route: GET /submissions/assignment/{assignment_id}/user/{user_id}/problem/{problem_id}/view/{choose}
submission_controller.php:52-109
public function index($assignment_id = NULL, $user_id = 'all', $problem_id = 'all', $choose = 'all')
{
    if (Assignment::find($assignment_id) == null)
    {
        return redirect()->route('submissions.index', [0, 'all', 'all', 'all']);
    }

    $assignment = Assignment::with('lops.users')->find($assignment_id);

    // Permission checks
    if (Auth::user()->role->name == 'admin'){
        // Admin can view anything
    }
    else if (in_array( Auth::user()->role->name, ['head_instructor', 'instructor'])
        && $assignment->id != 0
    ){
        if ($assignment->user != Auth::user()
            && !Auth::user()->lops()->with('assignments')->get()
                ->pluck('assignments')->collapse()->pluck('id')
                ->contains($assignment->id)
        ){
            abort(403, 'You can only view submissions for assignment you created or assignment belongs to one of your classes');
        }
    }

    $submissions = $assignment->submissions();
    
    // Filter by user
    if ( in_array( Auth::user()->role->name, ['student', 'guest']) 
        or ($assignment->id == 0 and !in_array( Auth::user()->role->name, ['admin']) ) )
    {
        $submissions = $submissions->where('user_id',Auth::user()->id);
    }
    else if ($user_id != 'all'){
        $submissions = $submissions->where('user_id',intval($user_id));
    }

    // Filter by final
    if ($choose == 'final'){
        $submissions = $submissions->where('is_final',1);
    }
    
    // Filter by problem
    if ($problem_id != 'all'){
        $submissions = $submissions->where('problem_id',intval($problem_id));
    }

    $submissions = $submissions->with(['language','user'])
        ->latest()
        ->paginate(Setting::get('results_per_page_all'));
        
    return view('submissions.list', [
        'submissions' => $submissions, 
        'assignment' => $assignment, 
        'user_id' => $user_id, 
        'problem_id' => $problem_id, 
        'choose' => $choose
    ]);
}

Submission Routes

MethodRouteActionPermission
GET/submissions/assignment/{aid}/user/{uid}/problem/{pid}/view/{choose}List submissionsowner, instructor+
GET/submissions/create/assignment/{aid}/problem/{pid}/{oldsub?}Create formparticipant
POST/submissions/storeSubmit codeparticipant
POST/submissions/get_templateGet code templateparticipant
POST/submissions/view_codeView submitted codeowner, instructor+
POST/submissions/view_statusView judging statusowner, instructor+
POST/submissions/selectSelect as finalowner
POST/submissions/rejudgeRejudge oneinstructor+
POST/submissions/rejudge_all_problems_assignmentRejudge manyinstructor+
GET/rejudge/{assignment}Rejudge interfacehead_instructor, admin

Best Practices

Submission

  • Test code locally first
  • Read problem requirements carefully
  • Check language is allowed
  • Verify file size limits

Code Quality

  • Follow template structure
  • Avoid banned keywords
  • Handle edge cases
  • Optimize for time/memory limits

Final Selection

  • Review all submissions before selecting
  • Consider late penalties
  • Verify test results
  • Select before deadline if possible

Rejudging

  • Use sparingly to avoid queue overload
  • Rejudge after fixing test cases
  • Notify students of rejudge
  • Monitor rejudge progress

Build docs developers (and LLMs) love