I am writing a custom Moodle (2.6) web service for taking up the moodle quiz, that requires to create an attempt or start an attempt. I written the API to start an attempt as well as get the questions for a particular quiz. However I m not able to figure out how to save the User responses for the question attempt. (Question attempt step data). Will appreciate if anyone can help me.
//Fetch all the user attempts
$attempts = quiz_get_user_attempts($quizObj->get_quizid(), 3, 'all', true);
//Get the last attempt
$lastattempt = end($attempts);
// Delete any previous preview attempts belonging to this user.
quiz_delete_previews($quizObj->get_quiz(), $USER->id);
//fetch the quiz usage object
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizObj->get_context());
$quba->set_preferred_behaviour($quizObj->get_quiz()->preferredbehaviour);
// Create the new attempt and initialize the question sessions
$timenow = time(); // Update time now, in case the server is running really slowly.
$attempt = quiz_create_attempt($quizObj, $attemptnumber, $lastattempt, $timenow, $quizObj->is_preview_user());
if ($lastAttemptStatus == quiz_attempt::FINISHED) {
$attemptnumber = $lastattempt->attempt + 1;
$attempt = quiz_start_new_attempt($quizObj, $quba, $attempt, $attemptnumber, $timenow);
} elseif ($lastAttemptStatus == quiz_attempt::IN_PROGRESS) {
$attempt = quiz_start_attempt_built_on_last($quba, $attempt, $lastattempt);
}
// It is here Lets say I would like to hardcode (get param) the responses from the user for a quiz for a specific question must go.. and I have add these responses as attempt step data..
$transaction = $DB->start_delegated_transaction();
$attempt = quiz_attempt_save_started($quizObj, $quba, $attempt);
quiz_fire_attempt_started_event($attempt, $quizObj);
$transaction->allow_commit();
$lastattempt = end($attempts);
For saving quiz attempts you need to call mod_quiz_save_attempt API
with question and answers response in parameters.
For eg:
http://**yourdomain.com**/webservice/rest/server.php?wstoken=**yourToken**&wsfunction=mod_quiz_save_attempt&moodlewsrestformat=json&attemptid=4545&data[0][name]=slots&data[0][value]=2&data[1][name]=q87124:2_:sequencecheck&data[1][value]=1&data[2][name]=q87124:2_answer&data[2][value]=2
For more details refer this link
Related
I am building a quiz program, using PHP, and MySQL.
I built the quiz program such that: the same file (Quiz.php) is called over and over again, but each time is selects a new question from the database.
The MySQL database contains a field "my_score". Any time the player answers a question correctly, a score of "1" is added to his current score. The player clicks "Next", and the SAME file is re-processed (Quiz.php)
Whenever he answers wrongly, nothing happens. The quiz simply exits.
I know that this issue is trivial, but I can't seem to get the logic right. I've tried using $_SESSION, but of course, this didn't work, as the variables are stored only when the user is logged on. If he logs off, and then comes back again to play, the "score" is reset to 1.
What I want is for the score to be updated in the database with each correct answer.
if ($selected_radio == $correct) { // if player selects correct answer..
$_SESSION['quiz_score'] += 1;
$my_score = $_SESSION['quiz_score'];
$update_score = "Update my_table
set my_score = $my_score where login = '".$_SESSION['login']."'";
$result_update_score = mysqli_query($conn,$update_score);
Then, I removed the $_SESSION variable, and simply used $my_score :
if ($selected_radio == $correct) {
$my_score += 1;
$update_score = "Update my_table
set my_score = $my_score where login = '".$_SESSION['login']."'";
It gave the same result.
have you tried using $update_score = "Update my_table
set my_score += $my_score where login = '".$_SESSION['login']."'";
so the points would just add up again to your stored score on the database?
I have a two PHP scripts that are loading many variable resources from APIs, causing the response times to as long as 2.2 seconds to 4 seconds. Any suggestions on how to decrease response times and increase efficiency would be very appreciated?
FIRST SCRIPT
require('path/to/local/API_2');
//Check if user has put a query and that it's not empty
if (isset($_GET['query']) && !empty($_GET['query'])) {
//$query is user input
$query = str_replace(" ", "+", $_GET['query']);
$query = addslashes($query);
//HTTP Request to API_1
//Based on $query
//Max Variable is ammount of results I want to get back in JSON format
$varlist = file_get_contents("http://ADRESS_OF_API_1.com?$query&max=10");
//Convert JSON to Array()
$varlist = json_decode($varlist, true);
//Initializing connection to API_2
$myAPIKey = 'KEY';
$client = new APIClient($myAPIKey, 'http://ADRESS_OF_API_2.com');
$Api = new API_FUNCTION($client);
$queries = 7;
//Go through $varlist and get data for each element in array then use it in HTML
//Proccess all 8 results from $varlist array()
for ($i = 0; $i <= $queries; ++$i) {
//Get info from API based on ID included in first API data
//I don't use all info, but I can't control what I get back.
$ALL_INFO = $Api->GET_FUNCTION_1($varlist[$i]['id']);
//Seperate $ALL_INFO into info I use
$varlist[$i]['INFO_1'] = $ALL_INFO['PATH_TO_INFO_1'];
$varlist[$i]['INFO_2'] = $ALL_INFO['PATH_TO_INFO_2'];
//Check if info exists
if($varlist[$i]['INFO_1']) {
//Concatenate information into HTML
$result.='
<div class="result">
<h3>'.$varlist[$i]['id'].'</h3>
<p>'.$varlist[$i]['INFO_1'].'</p>
<p>'.$varlist[$i]['INFO_2'].'</p>
</div>';
} else {
//In case of no result for specific Info ID increase
//Allows for 3 empty responses
++$queries;
}
}
} else {
//If user didn't enter a query, relocates them back to main page to enter one.
header("Location: http://websitename.com");
die();
}`
NOTE: $result equals HTML information from each time arround the loop.
NOTE: Almost all time is spent in the for ($i = 0; $i <= 7; ++$i)
loop.
SECOND SCRIPT
//Same API as before
require('path/to/local/API_2');
//Check if query is set and not empty
if (isset($_GET['query']) && !empty($_GET['query'])) {
//$query is specific $varlist[$i]['id'] for more information on that data
$query['id'] = str_replace(" ", "+", $_GET['query']);
$query['id'] = addslashes($query['id']);
//Initializing connection to only API used in this script
$myAPIKey = 'KEY';
$client = new APIClient($myAPIKey, 'http://ADRESS_OF_API_2.com');
$Api = new API_FUNCTION($client);
$ALL_INFO_1 = $Api->GET_FUNCTION_1($query['id']);
$query['INFO_ADRESS_1.1'] = $ALL_INFO_1['INFO_ADRESS_1'];
$query['INFO_ADRESS_1.2'] = $ALL_INFO_2['INFO_ADRESS_2'];
$ALL_INFO_2 = $Api->GET_FUNCTION_2($query['id']);
$query['INFO_ADRESS_2.1'] = $ALL_INFO_3['INFO_ADRESS_3'];
$ALL_INFO_3 = $Api->GET_FUNCTION_3($query['id']);
$query['INFO_ADRESS_3.1'] = $ALL_INFO_4['INFO_ADRESS_4'];
$ALL_INFO_4 = $Api->GET_FUNCTION_4($query['id']);
$query['INFO_ADRESS_4.1'] = $ALL_INFO_5['INFO_ADRESS_5'];
$query['INFO_ADRESS_4.2'] = $ALL_INFO_6['INFO_ADRESS_6'];
$ALL_INFO_5 = $Api->GET_FUNCTION_5($query['id']);
$query['INFO_ADRESS_5.1'] = $ALL_INFO_7['INFO_ADRESS_7'];
}
$result = All of the $query data from the API;
} else {
//If no query relocates them back to first PHP script page to enter one.
header("Location: http://websitename.com/search");
die();
}`
NOTE: Similiarly to the first script, most time is spent getting info
from the secondary API.
NOTE: In the second script, the first API is replaced by a single
specific variable from the first script page,so $varlist[$i]['id'] =
$query['id'].
NOTE: Again, $result is the HTML data.
You could also move the API calls out from your normal page load. Respond to the user with a generic page to show something is happening and then make an ajax request to query the APIs and respond with data. There really is no way to speed up an individual external request. Your best bet is to:
try to minimize the number of requests (even if it means you request a little more data once then filter out on your side vs sending multiple requests for a small subset of data).
cache any remaining requests and pull from cache.
respond with a small page to let the user know something is happening and make separate ajax requests for the queried data.
I am managing a pool of stateless session for a web service between users.
So when a user request web service he start session and response timeout is 5 sec, so he can hold session for 5 sec max. second user comes in and system check if there is available session then use it.
Now i have a problem here. when let say there is a session available, user A comes, system check if it was used more than 5 sec ago, given to user A, at the same time another user hit and system check if session used more than 4 sec ago, assigned to user B.
Now both user using same session and system fails on one.
I have tried select of update command as well to lock it.
I have tried update last used time as soon as it is selected by first user, but this didn't work (i think second user hitting system as the same time)
can someone advice on it.
code: check for session from db if available then pick it or insert a new
//get 25 sessions from database order by lastqueryDate
$session = $sessionObj->select('session', '*', '', 'LastQueryDate DESC', '25');
$available_session = array();
//if sessions available, get rows from getResult
if ($session) {
$session_Data = $sessionObj->getResult();
//now get session which is sitting there more than response time out
$available_session = $sessionObj->getAvailableSession($session_Data);
}
//if there is any, use it. otherwise create new session and save in database
if (!$available_session) {
$auth->securityAuthenticate();
$header = $auth->getHeaders();
$sequence = (int) $header['Session']->SequenceNumber + 1;
$values[] = $header['Session']->SessionId;
$values [] = $sequence;
$values [] = $header['Session']->SecurityToken;
$rows = "SessionID,SequenceNo,Security_token";
if ($sessionObj->insert('session', $values, $rows)) {
$available_session['Session']->SessionId = $header['Session']->SessionId;
$available_session['Session']->SequenceNumber = $sequence;
$available_session['Session']->SecurityToken = $header['Session']->SecurityToken;
}
}
function that check availability of session in db :
public function getAvailableSession($session_data) {
$available_session = array();
foreach ($session_data as $key) {
if (!is_array($key)) {
$key = $session_data;
}
$date = date('Y-m-d h:i:s a', time());
$now = new DateTime($date);
$last_query_time = new DateTime($key['LastQueryDate']);
$dteDiff = $last_query_time->diff($now);
$difference = $dteDiff->format("%H:%I:%S");
//if response time out is smaller than session used last. then pick it.
if (RTO <= $difference) {
$available_session['Session']->SessionId = $key['SessionID'];
$available_session['Session']->SequenceNumber = $key['SequenceNo'];
$available_session['Session']->SecurityToken = $key['Security_token'];
// run update to update lastqueryDate as its default value set to current time stamp
$session_value = $key['SessionID'];
$rows['SequenceNo'] = $key['SequenceNo'];
$where[0] = "SessionID";
$where[2] = "'" . $session_value . "'";
$this->update('session', $rows, $where);
return $available_session;
}
}
return false;
}
As soon as i find a session sitting idle for more than 5 sec i am updating database.
Open transaction. Issue "select ... for update" query for getting session data. Commit transaction at end of script.
My code:
$query = "INSERT IGNORE INTO `user_app` (app_id,imei, package_name, sdk_name, sdk_version, app_version)
VALUES
('".$appid."','".$imei."', '".$pkg."', '".$sdkn."', '".$sdkv."', '".$appv."')";
$mysqli -> query($query);
$id = $mysqli -> insert_id ; //get last user insert id
$idt = date('G:i:s', time());
$new = strtotime($idt);
include('requestad.php');
When a new user registered, he'll get an ad from requestad.php in json format. Insert id is save in a separate variable named $id, if a user again hit via application (as application invoke after every 30min ) then he'll get again json ad. I am trying to do some stuff like user get ad only once in whole 24hours, this is possible with insert id and insert time stamp. I am doing something like that:
if ( $new == time() ) {
include('requestad.php');
} elseif ( $new < time() ) {
echo 'nothing';
}
But problem is i didn't save exact execution time in variable and save time is necessary for comparison. Also, i have to send some blank to user if he again request for resource. Pl have a look on this and help me to produce optimal solution.
Still i didn't apply any logic yet. I can achieve this through store time which is static and compare it to time() which shows real time. Still i am looking this one
$store_time=$row['time'];//pick up the store time of user
$store_time=strtotime($row['time']);//and convert it in strtotime.if alredy did no need to do this.
$new=strtotime("-24 hours",strtotime(time()));//substract the time to -24 hours.
if($new==$store_time)
{
//your code
}
else
{
//your code
}
I would like to know if there is a way to remove rows or filter a recordset or collection.
For example, if I have two tables: one for questions and one for the answer choices. The questions belong to different forms. Questions 1-10 belongs to form a, 11-20 belong to form b. Depending on the answers of the previous questions, certain questions may or may not show up, and certain answers later on may or may not show up. Instead of constantly hitting the database, I want to cache the recordset or collection of questions belonging to each form into memory and filter off of the in memory set of questions per session.
This way each user will only hit the database once, at the beginning of their session, instead of every time they click on next.
The Collection object used by the models is extended from lithium\util\Collection which provides a method for filtering an existing collection and returning a new one based on a user provided closure.
$newQuestions = $oldQuestions->find(function($question) {
if (your new criteria) {
return true;
}
return false;
});
Simply determine the criteria you wish to apply and perform the filtering in the closure. Once it runs you should have a new Collection object with only the records that matched.
After you get a Recordset or Collection from your database, you can execute a couple of filters on it. See the lithium\util\Collection for more info.
An example would be
$questions = Questions::all();
$form_questions = $questions->find(function($question) {
if($query->form == 'b') {
return true;
}
return false;
}), true);
To handle keeping these questions persist between page requests, look into lithium\storage\Session.
It's unlikely that simple 'once-per-page' database calls will put much of a strain on your server unless you have truely terrific traffic, but if you do want to do this, the easiest way to do this will be to cache this information in the PHP $_SESSION superglobal when a user logs in. Assuming you've set PHP to use filesystem storage (though even if you use database session storage it will have only a tiny affect on performance), you'll have your questions stored in super fast-to-access files which are already pre-built to be unique to each specific user. As soon as a script is loaded, the session file is automatically read into memory and you can access any of the information from there.
EXAMPLE:
Assuming that your questions table has columns question_number and question_text and your answers table has the columns question_number and answer_text:
<?php
//on login:
//first get the answer array, so we can use it in the logic below:
$query = mysql_query('SELECT * FROM `questions` WHERE [criteria]',[connection identifier]) or die(mysql_error());
if (!mysql_num_rows($query)){
die("No questions!");
}
$answer_array = array();
//create a keyed array that you can access by question number
while($row=mysql_fetch_array($query)){
$answer_array[$row['question_number']] = $row['answer_text'];
}
//now get the questions and put everything into the session variable
$query = mysql_query('SELECT * FROM `questions` WHERE [criteria]',[connection identifier]) or die(mysql_error());
if (!mysql_num_rows($query)){
die("No questions!");
}
//loop through the results and generate session arrays we can work with later
while($row = mysql_fetch_array($query)){
//assign the question to the correct form:
if ($row['question_number']<=10){
$session_key = 'form_a';
} elseif($row['question_number']<=20){
$session_key = 'form_b';
} elseif($row['question_number']<=30){
$session_key = 'form_c';
} else {
$session_key = 'form_d';
}
//if the session variable does exist yet, create it:
if (!isset($_SESSION[$session_key])){
$_SESSION[$session_key] = array();
}
//get the existing answer if it exists, otherwise leave the answer blank:
$my_answer = "";
if(isset($answer_array[$row['question_number']])){
$my_answer = $answer_array[$row['question_number']];
}
//add this question array as a child array element in the session array, keyed by the question number
$_SESSION[$session_key][$row['question_number']] = array(
'question' => $row,
'answer' => $my_answer
);
}
Now, if we're loading Form B, for instance, we can just read it out of the session array $_SESSION['form_b'] and perform any logical switches we want based on the answers to previous questions:
$html = "";
foreach($_SESSION['form_b'] as $question_number => $data){
//perform any logic, for instance, if question 2 from form a is equal to '5', don't show question 3 on form B:
switch($question_number){
case '3': if ($_SESSION['form_a']['2']['answer']=='5'){ continue 2; }; break;
}
//add the question to the form, and populate the answer if they already answered it earlier:
$html .= "<label>".$data['question']."<input type='text' value=\"".$data['answer']."\" name='question_".$question_number."' /></label>";
}
Then, when you submit each form, in addition to updating the mysql answers table, you'll also want to update your _SESSION array. For instance, if you're submitting form B via POST:
$form = 'form_b';
foreach($_POST as $key=>$value){
if (substr($key,0,strlen('question_')!='question_'){
continue;
}
$number = str_replace('question_','',$key); //this will give us the question number
$saved = add_answer($number,$value); //call the function to insert the new answer into the database (this is a dummy function, and please make sure to escape your variables
if ($saved){//assuming it saved:
$_SESSION[$form ][$number]['answer']=$value; //now we've updated the session array as well.
}
}