public static function process_attempt($attemptid, $data, $finishattempt = false, $timeup = false, $preflightdata = array()) {

        $warnings = array();

        $params = array(
            'attemptid' => $attemptid,
            'data' => $data,
            'finishattempt' => $finishattempt,
            'timeup' => $timeup,
            'preflightdata' => $preflightdata,
        );
        $params = self::validate_parameters(self::process_attempt_parameters(), $params);

        // Do not check access manager rules and evaluate fail if overdue.
        $attemptobj = quiz_attempt::create($params['attemptid']);
        $failifoverdue = !($attemptobj->get_quizobj()->get_quiz()->overduehandling == 'graceperiod');

        list($attemptobj, $messages) = self::validate_attempt($params, false, $failifoverdue);

        // Create the $_POST object required by the question engine.
        $_POST = array();
        foreach ($params['data'] as $element) {
            $_POST[$element['name']] = $element['value'];
        }
        $timenow = time();
        $finishattempt = $params['finishattempt'];
        $timeup = $params['timeup'];

        $result = array();
        // Update the timemodifiedoffline field.
        $attemptobj->set_offline_modified_time($timenow);
        $result['state'] = $attemptobj->process_attempt($timenow, $finishattempt, $timeup, 0);

        $result['warnings'] = $warnings;
        return $result;
    }