mirror of
https://github.com/ACSPRI/queXS
synced 2024-04-02 12:12:16 +00:00
Alter case sorting/selecting SQL to consider timeslots if set. If set, only select a case if a current timeslot exists and the number of calls in the slot is less than the maximum number of calls made in any other slot
2288 lines
65 KiB
PHP
2288 lines
65 KiB
PHP
<?php
|
|
/**
|
|
* Operator functions for interacting with the database and getting/storing state
|
|
*
|
|
*
|
|
* This file is part of queXS
|
|
*
|
|
* queXS is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* queXS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with queXS; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*
|
|
* @author Adam Zammit <adam.zammit@deakin.edu.au>
|
|
* @copyright Deakin University 2007,2008
|
|
* @package queXS
|
|
* @subpackage functions
|
|
* @link http://www.deakin.edu.au/dcarf/ queXS was writen for DCARF - Deakin Computer Assisted Research Facility
|
|
* @license http://opensource.org/licenses/gpl-2.0.php The GNU General Public License (GPL) Version 2
|
|
*
|
|
*
|
|
*
|
|
* @todo Add session data to reduce calls to the database
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Configuration file
|
|
*/
|
|
include_once(dirname(__FILE__).'/../config.inc.php');
|
|
|
|
/**
|
|
* Database file
|
|
*/
|
|
include_once(dirname(__FILE__).'/../db.inc.php');
|
|
|
|
/**
|
|
* Creates a random sequence of characters
|
|
*
|
|
* @param mixed $length Length of resulting string
|
|
* @param string $pattern To define which characters should be in the resulting string
|
|
*
|
|
* From Limesurvey
|
|
*/
|
|
function sRandomChars($length = 15,$pattern="23456789abcdefghijkmnpqrstuvwxyz")
|
|
{
|
|
$patternlength = strlen($pattern)-1;
|
|
for($i=0;$i<$length;$i++)
|
|
{
|
|
if(isset($key))
|
|
$key .= $pattern{rand(0,$patternlength)};
|
|
else
|
|
$key = $pattern{rand(0,$patternlength)};
|
|
}
|
|
return $key;
|
|
}
|
|
|
|
/**
|
|
* Check if the project associated with this case is using
|
|
* questionnaire availability
|
|
*
|
|
* @param int $case_id
|
|
*
|
|
* @return boolean True if using case availability, false if not
|
|
* @author Adam Zammit <adam.zammit@acspri.org.au>
|
|
* @since 2011-07-01
|
|
*/
|
|
function is_using_availability($case_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT count(qa.questionnaire_id) as cc
|
|
FROM `case` as c, questionnaire_availability as qa
|
|
WHERE qa.questionnaire_id = c.questionnaire_id
|
|
AND c.case_id = $case_id";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if ($rs['cc'] > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return if chat is enabled for this operator
|
|
*
|
|
* @param int $operator_id the operator id
|
|
*
|
|
* @return bool True if enabled, false if not
|
|
* @author Adam Zammit <adam.zammit@acspri.org.au>
|
|
* @since 2013-07-16
|
|
*/
|
|
function operator_chat_enabled($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT chat_enable
|
|
FROM `operator`
|
|
WHERE operator_id = '$operator_id'";
|
|
|
|
$c = $db->GetOne($sql);
|
|
|
|
if ($c == 1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return if VOIP is enabled on an operator by operator basis
|
|
* Will always return false if VOIP is globally disabled
|
|
*
|
|
* @param int $operator_id the operator id
|
|
* @return bool True if enabled, false if not
|
|
*/
|
|
function is_voip_enabled($operator_id)
|
|
{
|
|
if (VOIP_ENABLED == false)
|
|
return false;
|
|
|
|
global $db;
|
|
|
|
$sql = "SELECT o.voip
|
|
FROM `operator` as o, `extension` as e
|
|
WHERE o.operator_id = '$operator_id'
|
|
AND e.current_operator_id = o.operator_id";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if (isset($rs['voip']) && $rs['voip'] == '1')
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return the period of the day for the respondent
|
|
*
|
|
* @param int $respondent_id The respondent id
|
|
* @return string Either morning, afternoon or evening based on the respondents time zone
|
|
*
|
|
*/
|
|
function get_period_of_day($respondent_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT HOUR(CONVERT_TZ(NOW(),'System',Time_zone_name)) as h
|
|
FROM respondent
|
|
WHERE respondent_id = '$respondent_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
$hour = 10;
|
|
if (!empty($rs))
|
|
$hour = $rs['h'];
|
|
|
|
if ($hour < 12) return T_("morning");
|
|
if ($hour < 17) return T_("afternoon");
|
|
return T_("evening");
|
|
}
|
|
|
|
|
|
/**
|
|
* Get information from the sample
|
|
*
|
|
* @param string $variable The bit of information from the sample
|
|
* @param int $case_id The case id
|
|
* @return string The information or a blank string if none found
|
|
*
|
|
*/
|
|
function get_sample_variable($variable,$case_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT s.val as r
|
|
FROM sample_var as s
|
|
JOIN `case` as c on (c.case_id = '$case_id' and s.sample_id = c.sample_id)
|
|
WHERE s.var = '$variable'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
|
|
if (empty($rs)) return "";
|
|
|
|
return $rs['r'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Get information about the operator
|
|
*
|
|
* @param string $variable The bit of information about the operator (eg firstName)
|
|
* @param int $operator_id The operator id
|
|
* @return string The information or a blank string if none found
|
|
*
|
|
*/
|
|
function get_operator_variable($variable,$operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT `$variable` as r
|
|
FROM operator
|
|
WHERE operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if (empty($rs)) return "";
|
|
|
|
return $rs['r'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Get information about the respondent
|
|
*
|
|
* @param string $variable The bit of information about the respondent (eg firstName)
|
|
* @param int $respondent_id The respondent id
|
|
* @return string The information or a blank string if none found
|
|
*
|
|
*/
|
|
function get_respondent_variable($variable,$respondent_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT `$variable` as r
|
|
FROM respondent
|
|
WHERE respondent_id = '$respondent_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
|
|
if (empty($rs)) return "";
|
|
|
|
return $rs['r'];
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Replace placeholders in a string with data for this case/operator
|
|
*
|
|
* @param string $string The string
|
|
* @param int $operator_id The operator id
|
|
* @param int $case_id The case id
|
|
* @return string The string with replaced text
|
|
*
|
|
*/
|
|
function template_replace($string,$operator_id,$case_id)
|
|
{
|
|
$respondent_id = get_respondent_id(get_call_attempt($operator_id,false));
|
|
|
|
while (stripos($string, "{Respondent:") !== false)
|
|
{
|
|
$answreplace=substr($string, stripos($string, "{Respondent:"), stripos($string, "}", stripos($string, "{Respondent:"))-stripos($string, "{Respondent:")+1);
|
|
$answreplace2=substr($answreplace, 12, stripos($answreplace, "}", stripos($answreplace, "{Respondent:"))-12);
|
|
$answreplace3=get_respondent_variable($answreplace2,$respondent_id);
|
|
$string=str_replace($answreplace, $answreplace3, $string);
|
|
}
|
|
|
|
|
|
while (stripos($string, "{Operator:") !== false)
|
|
{
|
|
$answreplace=substr($string, stripos($string, "{Operator:"), stripos($string, "}", stripos($string, "{Operator:"))-stripos($string, "{Operator:")+1);
|
|
$answreplace2=substr($answreplace, 10, stripos($answreplace, "}", stripos($answreplace, "{Operator:"))-10);
|
|
$answreplace3=get_operator_variable($answreplace2,$operator_id);
|
|
$string=str_replace($answreplace, $answreplace3, $string);
|
|
}
|
|
|
|
while (stripos($string, "{Sample:") !== false)
|
|
{
|
|
$answreplace=substr($string, stripos($string, "{Sample:"), stripos($string, "}", stripos($string, "{Sample:"))-stripos($string, "{Sample:")+1);
|
|
$answreplace2=substr($answreplace, 8, stripos($answreplace, "}", stripos($answreplace, "{Sample:"))-8);
|
|
$answreplace3=get_sample_variable($answreplace2,$case_id);
|
|
$string=str_replace($answreplace, $answreplace3, $string);
|
|
}
|
|
|
|
while (stripos($string, "{PeriodOfDay") !== false)
|
|
{
|
|
$answreplace=substr($string, stripos($string, "{PeriodOfDay"), stripos($string, "}", stripos($string, "{PeriodOfDay"))-stripos($string, "{PeriodOfDay")+1);
|
|
$answreplace3=get_period_of_day($respondent_id);
|
|
$string=str_replace($answreplace, $answreplace3, $string);
|
|
}
|
|
|
|
|
|
return $string;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return true if the current questionnaire has respondent selection
|
|
* enabled
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return bool|int True if respondent selection enabled, lime_sid if rs enabled with limesurvey otherwise false
|
|
*
|
|
*/
|
|
function is_respondent_selection($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$questionnaire_id = get_questionnaire_id($operator_id);
|
|
|
|
if (!$questionnaire_id)
|
|
return false;
|
|
|
|
$sql = "SELECT respondent_selection, lime_rs_sid
|
|
FROM questionnaire
|
|
WHERE questionnaire_id = '$questionnaire_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in is_respondent_selection"; exit; }
|
|
|
|
if (!$db->CompleteTrans())
|
|
return false;
|
|
|
|
if (!$rs) return false;
|
|
if ($rs['respondent_selection'] == 1){
|
|
if ($rs['lime_rs_sid'] > 0)
|
|
{
|
|
return $rs['lime_rs_sid'];
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Add a case to the system based on a sample record
|
|
*
|
|
* @param int $sample_id The sample id
|
|
* @param int $questionnaire_id The questionnaire id
|
|
* @param int $operator_id The operator id (Default NULL)
|
|
* @param int $testing 0 if a live case otherwise 1 for a testing case
|
|
*
|
|
* @return int The case id
|
|
*/
|
|
function add_case($sample_id,$questionnaire_id,$operator_id = "NULL",$testing = 0)
|
|
{
|
|
global $db;
|
|
|
|
$token = sRandomChars();
|
|
|
|
$sql = "INSERT INTO `case` (case_id, sample_id, questionnaire_id, last_call_id, current_operator_id, current_call_id, current_outcome_id,token)
|
|
VALUES (NULL, $sample_id, $questionnaire_id, NULL, $operator_id, NULL, 1, '$token')";
|
|
|
|
$db->Execute($sql);
|
|
|
|
$case_id = $db->Insert_ID();
|
|
|
|
//if this sample is set as testing, assign internal numbers as numbers
|
|
if ($testing == 1)
|
|
{
|
|
$db->Execute("SET @row := 0");
|
|
|
|
$sql = "INSERT INTO contact_phone (case_id,priority,phone,description)
|
|
SELECT $case_id as case_id,@row := @row + 1 AS priority,SUBSTRING_INDEX(e.extension,'/',-1) as phone, CONCAT(o.firstName, ' ', o.lastName)
|
|
FROM operator as o, `extension` as e
|
|
WHERE o.enabled = 1
|
|
AND e.current_operator_id = o.operator_id";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
else
|
|
{
|
|
//add any phone numbers to contact phone
|
|
|
|
//$db->Execute("SET @row := 0");
|
|
|
|
$sql = "SELECT val as phone
|
|
FROM sample_var
|
|
WHERE sample_id = '$sample_id'
|
|
AND val is NOT NULL
|
|
AND val != \"\"
|
|
AND (`type` = 2 or `type` = 3)
|
|
ORDER BY `type` DESC";
|
|
|
|
$r5 = $db->GetAll($sql);
|
|
|
|
if (!empty($r5))
|
|
{
|
|
$i = 1;
|
|
foreach ($r5 as $r5v)
|
|
{
|
|
$tnum = preg_replace("/[^0-9]/", "",$r5v['phone']);
|
|
if (empty($tnum)) $tnum = "312345678"; //handle error condition
|
|
$sql = "INSERT INTO contact_phone (case_id,priority,phone,description)
|
|
VALUES ($case_id,$i,$tnum,'')";
|
|
$db->Execute($sql);
|
|
$i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$sql = "INSERT INTO contact_phone (case_id,priority,phone,description)
|
|
VALUES ($case_id,1,312345678,'test only')";
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
}
|
|
|
|
//add respondent details to respondent (if such details exist in the sample)
|
|
|
|
$sql = "INSERT INTO respondent (case_id,firstName,lastName,Time_zone_name)
|
|
SELECT $case_id as case_id, IFNULL(s1.val,'') as firstName, IFNULL(s2.val,'') as lastName, s3.Time_zone_name as Time_zone_name
|
|
FROM sample as s3
|
|
LEFT JOIN sample_var as s2 on (s2.sample_id = '$sample_id' and s2.type = 7)
|
|
LEFT JOIN sample_var as s1 on (s1.sample_id = '$sample_id' and s1.type = 6)
|
|
WHERE s3.sample_id = '$sample_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
|
|
//add resopndent to Lime Survey token table for this questionnaire
|
|
|
|
//first we need to get the limesurvey survey id
|
|
|
|
if (!$db->HasFailedTrans()) //if the transaction hasn't failed
|
|
{
|
|
$sql = "SELECT lime_sid
|
|
FROM questionnaire
|
|
WHERE questionnaire_id = '$questionnaire_id'";
|
|
|
|
$lime_sid = $db->GetOne($sql);
|
|
|
|
if ($lime_sid)
|
|
{
|
|
$sql = "INSERT INTO ".LIME_PREFIX."tokens_$lime_sid (tid,firstname,lastname,email,token,language,sent,completed,mpid)
|
|
VALUES (NULL,'','','','$token','".DEFAULT_LOCALE."','N','N',NULL)";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
}
|
|
|
|
return $case_id;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the current or next case id
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @param bool $create True if a case can be created
|
|
* @return bool|int False if no case available else the case_id
|
|
*/
|
|
function get_case_id($operator_id, $create = false)
|
|
{
|
|
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
/**
|
|
* See if case already assigned
|
|
*/
|
|
$sql = "SELECT case_id
|
|
FROM `case`
|
|
WHERE current_operator_id = '$operator_id'";
|
|
|
|
$r1 = $db->GetRow($sql);
|
|
|
|
|
|
$case_id = false;
|
|
|
|
if (empty($r1))
|
|
{
|
|
$sql = "SELECT o.next_case_id
|
|
FROM `operator` as o, `case` as c
|
|
WHERE o.operator_id = '$operator_id'
|
|
AND c.case_id = o.next_case_id
|
|
AND c.current_operator_id IS NULL";
|
|
|
|
$rnc = $db->GetRow($sql);
|
|
|
|
$sql = "SELECT cq.case_id, cq.case_queue_id
|
|
FROM case_queue as cq, `case` as c
|
|
WHERE cq.operator_id = '$operator_id'
|
|
AND cq.case_id = c.case_id
|
|
AND c.current_operator_id IS NULL
|
|
ORDER BY cq.sortorder ASC
|
|
LIMIT 1";
|
|
|
|
$sq = $db->GetRow($sql);
|
|
|
|
if (isset($rnc['next_case_id']) && !empty($rnc['next_case_id']))
|
|
{
|
|
$case_id = $rnc['next_case_id'];
|
|
|
|
$sql = "UPDATE `case`
|
|
SET current_operator_id = '$operator_id'
|
|
WHERE current_operator_id IS NULL
|
|
AND case_id = '$case_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
//should fail transaction if already assigned to another case
|
|
if ($db->Affected_Rows() != 1)
|
|
{
|
|
$db->FailTrans();
|
|
}
|
|
else
|
|
{
|
|
//remove next case setting
|
|
$sql = "UPDATE `operator`
|
|
SET next_case_id = NULL
|
|
WHERE operator_id = '$operator_id'";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
}
|
|
else if (isset($sq['case_id']) && !empty($sq['case_id']))
|
|
{
|
|
$case_id = $sq['case_id'];
|
|
$case_queue_id = $sq['case_queue_id'];
|
|
|
|
$sql = "UPDATE `case`
|
|
SET current_operator_id = '$operator_id'
|
|
WHERE current_operator_id IS NULL
|
|
AND case_id = '$case_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
//should fail transaction if already assigned to another case
|
|
if ($db->Affected_Rows() != 1)
|
|
{
|
|
$db->FailTrans();
|
|
}
|
|
else
|
|
{
|
|
//remove case from queue and update sortorder
|
|
$sql = "DELETE FROM case_queue
|
|
WHERE case_queue_id = '$case_queue_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
$sql = "SELECT case_queue_id
|
|
FROM case_queue
|
|
WHERE operator_id = '$operator_id'
|
|
ORDER BY sortorder ASC";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
$sortorder = 1;
|
|
foreach($rs as $r)
|
|
{
|
|
$sql = "UPDATE case_queue
|
|
SET sortorder = '$sortorder'
|
|
WHERE case_queue_id = '{$r['case_queue_id']}'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
$sortorder++;
|
|
}
|
|
}
|
|
}
|
|
else if ($create)
|
|
{
|
|
$systemsort = get_setting('systemsort');
|
|
|
|
if ($systemsort)
|
|
{
|
|
//Just make sure that this case should go to this operator (assigned to this project and skill)
|
|
//Also check if this is an exclusive appointment and that the questionnaire is enabled
|
|
$sql = "SELECT c.case_id as caseid
|
|
FROM `case` as c
|
|
JOIN operator_questionnaire AS oq ON (oq.operator_id = '$operator_id' AND oq.questionnaire_id = c.questionnaire_id)
|
|
JOIN questionnaire as q ON (q.questionnaire_id = c.questionnaire_id AND q.enabled = 1)
|
|
JOIN outcome as ou ON (ou.outcome_id = c.current_outcome_id)
|
|
JOIN operator_skill as os ON (os.operator_id = '$operator_id' AND os.outcome_type_id = ou.outcome_type_id)
|
|
LEFT JOIN appointment as apn on (apn.case_id = c.case_id AND apn.completed_call_id is NULL AND (CONVERT_TZ(NOW(),'System','UTC') >= apn.start) AND (CONVERT_TZ(NOW(),'System','UTC') <= apn.end))
|
|
WHERE c.sortorder IS NOT NULL
|
|
AND c.current_operator_id IS NULL
|
|
AND (apn.require_operator_id IS NULL OR apn.require_operator_id = '$operator_id')
|
|
ORDER BY c.sortorder ASC
|
|
LIMIT 1";
|
|
|
|
}
|
|
else
|
|
{
|
|
/**
|
|
* find a case that:
|
|
* Has not been called in the last x hours based on last outcome
|
|
* Is available for this operator
|
|
* Has no appointments scheduled in the future (can also check if outcome is appointment)
|
|
* Nobody else is servicing the call at the moment
|
|
* The case is not referred to the supervisor and the operator is not the supervisor
|
|
* The case is not on a refusal outcome and the operator is not a refusal converter
|
|
* Give priority if there is an appointment scheduled now
|
|
* If restricted to shift times to work, make sure we are in those
|
|
* If restricted to respondent call times, make sure we are in those
|
|
* Only assign if outcome type is assigned to the operator
|
|
* Has not reached the quota
|
|
* Is part of an enabled questionnaire
|
|
*
|
|
*
|
|
* THINGS TO ADD:
|
|
*
|
|
* @todo also could check the respondent_not_available table to see if now is a "bad time" to call
|
|
*/
|
|
|
|
$sql = "SELECT c.case_id as caseid
|
|
FROM `case` as c
|
|
LEFT JOIN `call` as a on (a.call_id = c.last_call_id)
|
|
JOIN (sample as s, sample_import as si) on (s.sample_id = c.sample_id and si.sample_import_id = s.import_id)
|
|
JOIN (questionnaire_sample as qs, operator_questionnaire as o, questionnaire as q, operator as op, outcome as ou) on (c.questionnaire_id = q.questionnaire_id and q.enabled = 1 and op.operator_id = '$operator_id' and qs.sample_import_id = s.import_id and o.operator_id = op.operator_id and o.questionnaire_id = qs.questionnaire_id and q.questionnaire_id = o.questionnaire_id and ou.outcome_id = c.current_outcome_id)
|
|
LEFT JOIN shift as sh on (sh.questionnaire_id = q.questionnaire_id and (CONVERT_TZ(NOW(),'System','UTC') >= sh.start) AND (CONVERT_TZ(NOW(),'System','UTC') <= sh.end))
|
|
LEFT JOIN appointment as ap on (ap.case_id = c.case_id AND ap.completed_call_id is NULL AND (ap.start > CONVERT_TZ(NOW(),'System','UTC')))
|
|
LEFT JOIN appointment as apn on (apn.case_id = c.case_id AND apn.completed_call_id is NULL AND (CONVERT_TZ(NOW(),'System','UTC') >= apn.start) AND (CONVERT_TZ(NOW(),'System','UTC') <= apn.end))
|
|
LEFT JOIN call_restrict as cr on (cr.day_of_week = DAYOFWEEK(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) and TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) >= cr.start and TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) <= cr.end)
|
|
LEFT JOIN questionnaire_sample_exclude_priority AS qsep ON (qsep.questionnaire_id = c.questionnaire_id AND qsep.sample_id = c.sample_id)
|
|
LEFT JOIN case_availability AS casa ON (casa.case_id = c.case_id)
|
|
LEFT JOIN availability AS ava ON (ava.availability_group_id = casa.availability_group_id)
|
|
LEFT JOIN questionnaire_timeslot AS qast ON (qast.questionnaire_id = c.questionnaire_id)
|
|
JOIN operator_skill as os on (os.operator_id = op.operator_id and os.outcome_type_id = ou.outcome_type_id)
|
|
WHERE c.current_operator_id IS NULL
|
|
AND (casa.case_id IS NULL OR (ava.day_of_week = DAYOFWEEK(CONVERT_TZ(NOW(),'System',s.Time_zone_name)) AND TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) >= ava.start AND TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) <= ava.end ))
|
|
AND (qast.questionnaire_id IS NULL OR ((SELECT COUNT(*) FROM availability WHERE availability.availability_group_id = qast.availability_group_id AND (availability.day_of_week = DAYOFWEEK(CONVERT_TZ(NOW(),'System',s.Time_zone_name)) AND TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) >= availability.start AND TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) <= availability.end)) >= 1 AND (SELECT COUNT(call_id) FROM `call`, availability WHERE call.case_id = c.case_id AND call.outcome_id != 1 AND (availability.availability_group_id = qast.availability_group_id AND (availability.day_of_week = DAYOFWEEK(CONVERT_TZ(call.start,'UTC',s.Time_zone_name)) AND TIME(CONVERT_TZ(call.start, 'UTC' , s.Time_zone_name)) >= availability.start AND TIME(CONVERT_TZ(call.start, 'UTC' , s.Time_zone_name)) <= availability.end))) < (SELECT COUNT(call.call_id) FROM `call`, questionnaire_timeslot, availability WHERE call.case_id = c.case_id AND call.outcome_id != 1 AND questionnaire_timeslot.questionnaire_id = c.questionnaire_id AND (availability.availability_group_id = questionnaire_timeslot.availability_group_id AND (availability.day_of_week = DAYOFWEEK(CONVERT_TZ(call.start,'UTC',s.Time_zone_name)) AND TIME(CONVERT_TZ(call.start, 'UTC' , s.Time_zone_name)) >= availability.start AND TIME(CONVERT_TZ(call.start, 'UTC' , s.Time_zone_name)) <= availability.end)) GROUP BY availability.availability_group_id ORDER BY COUNT(call.call_id) DESC LIMIT 1)))
|
|
AND (a.call_id is NULL or (a.end < CONVERT_TZ(DATE_SUB(NOW(), INTERVAL ou.default_delay_minutes MINUTE),'System','UTC')))
|
|
AND ap.case_id is NULL
|
|
AND ((qsep.questionnaire_id is NULL) or qsep.exclude = 0)
|
|
AND !(q.restrict_work_shifts = 1 AND sh.shift_id IS NULL AND os.outcome_type_id != 2)
|
|
AND !(si.call_restrict = 1 AND cr.day_of_week IS NULL AND os.outcome_type_id != 2)
|
|
AND ((apn.appointment_id IS NOT NULL) or qs.call_attempt_max = 0 or ((SELECT count(*) FROM call_attempt WHERE case_id = c.case_id) < qs.call_attempt_max))
|
|
AND ((apn.appointment_id IS NOT NULL) or qs.call_max = 0 or ((SELECT count(*) FROM `call` WHERE case_id = c.case_id) < qs.call_max))
|
|
AND (apn.require_operator_id IS NULL OR apn.require_operator_id = '$operator_id')
|
|
AND (SELECT count(*) FROM `questionnaire_sample_quota` WHERE questionnaire_id = c.questionnaire_id AND sample_import_id = s.import_id AND quota_reached = 1) = 0
|
|
ORDER BY IF(ISNULL(apn.end),1,0),apn.end ASC, a.start ASC, qsep.priority DESC
|
|
LIMIT 1";
|
|
|
|
//apn.appointment_id contains the id of an appointment if we are calling on an appointment
|
|
}
|
|
$r2 = $db->GetRow($sql);
|
|
|
|
if (empty($r2))
|
|
{
|
|
|
|
if ($systemsort)
|
|
{
|
|
//Just make sure that this case should go to this operator (assigned to this project and skill)
|
|
$sql = "SELECT qsep.sample_id as sample_id, qsep.questionnaire_id as questionnaire_id, q.testing as testing
|
|
FROM questionnaire_sample_exclude_priority as qsep
|
|
JOIN operator_skill as os ON (os.operator_id = '$operator_id' AND os.outcome_type_id = 1)
|
|
JOIN operator_questionnaire AS oq ON (oq.operator_id = '$operator_id' AND oq.questionnaire_id = qsep.questionnaire_id)
|
|
JOIN questionnaire as q ON (q.questionnaire_id = qsep.questionnaire_id and q.enabled = 1)
|
|
LEFT JOIN `case` as c ON (c.sample_id = qsep.sample_id AND c.questionnaire_id = qsep.questionnaire_id)
|
|
WHERE qsep.sortorder IS NOT NULL
|
|
AND c.case_id IS NULL
|
|
ORDER BY qsep.sortorder ASC
|
|
LIMIT 1";
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/**
|
|
* If no case found, we must draw the next available case from the sample
|
|
* only if no case due to lack of cases to call not out of shift time/etc and
|
|
* only draw cases that are new (Temporary outcome_type_id) - this makes sure that we are not drawing
|
|
* a case just because the operator doesn't have access to temporary outcome id's.
|
|
*
|
|
*
|
|
* Method:
|
|
* next available that has not been assigned
|
|
* if none available - return false? report to operator that no one available to call at currenet settings
|
|
*
|
|
*/
|
|
|
|
|
|
$sql = "SELECT s.sample_id as sample_id,c.case_id as case_id,qs.questionnaire_id as questionnaire_id,CONVERT_TZ(NOW(), 'System' , s.Time_zone_name) as resptime, q.testing as testing
|
|
FROM sample as s
|
|
JOIN (questionnaire_sample as qs, operator_questionnaire as o, questionnaire as q, operator as op, sample_import as si, operator_skill as os) on (op.operator_id = '$operator_id' and qs.sample_import_id = s.import_id and o.operator_id = op.operator_id and o.questionnaire_id = qs.questionnaire_id and q.questionnaire_id = o.questionnaire_id and si.sample_import_id = s.import_id and os.operator_id = op.operator_id and os.outcome_type_id = 1 and q.enabled = 1 and qs.allow_new = 1)
|
|
LEFT JOIN `case` as c on (c.sample_id = s.sample_id and c.questionnaire_id = qs.questionnaire_id)
|
|
LEFT JOIN call_restrict as cr on (cr.day_of_week = DAYOFWEEK(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) and TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) >= cr.start and TIME(CONVERT_TZ(NOW(), 'System' , s.Time_zone_name)) <= cr.end)
|
|
LEFT JOIN shift as sh on (sh.questionnaire_id = q.questionnaire_id and (CONVERT_TZ(NOW(),'System','UTC') >= sh.start) AND (CONVERT_TZ(NOW(),'System','UTC') <= sh.end))
|
|
LEFT JOIN questionnaire_sample_exclude_priority AS qsep ON (qsep.questionnaire_id = qs.questionnaire_id AND qsep.sample_id = s.sample_id)
|
|
|
|
WHERE c.case_id is NULL
|
|
AND ((qsep.questionnaire_id IS NULL) or qsep.exclude = 0)
|
|
AND !(q.restrict_work_shifts = 1 AND sh.shift_id IS NULL)
|
|
AND !(si.call_restrict = 1 AND cr.day_of_week IS NULL)
|
|
AND (SELECT count(*) FROM `questionnaire_sample_quota` WHERE questionnaire_id = qs.questionnaire_id AND sample_import_id = s.import_id AND quota_reached = 1) = 0
|
|
ORDER BY qsep.priority DESC, rand() * qs.random_select, s.sample_id
|
|
LIMIT 1";
|
|
}
|
|
|
|
$r3 = $db->GetRow($sql);
|
|
|
|
|
|
/**
|
|
* If the above statement returns no rows, then there are no cases to be added to the sample at this time
|
|
* We could do a select of how many are actually available to reassure the operator that the sample has not been exhausted
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
/**
|
|
* Now we have to add phone numbers to the contact_phone table, the case to the case table,
|
|
* assign this case to this operator
|
|
*/
|
|
|
|
if (!empty($r3))
|
|
{
|
|
$case_id = add_case($r3['sample_id'],$r3['questionnaire_id'],$operator_id,$r3['testing']);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$case_id = $r2['caseid'];
|
|
|
|
$sql = "UPDATE `case`
|
|
SET current_operator_id = '$operator_id'
|
|
WHERE current_operator_id IS NULL
|
|
AND case_id = '$case_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
//should fail transaction if already assigned to another case
|
|
if ($db->Affected_Rows() != 1)
|
|
{
|
|
$db->FailTrans();
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$case_id = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$case_id = $r1['case_id'];
|
|
|
|
}
|
|
|
|
if ($db->HasFailedTrans())
|
|
{
|
|
error_log("FAILED in get_case_id for case $case_id",0);
|
|
$case_id = false; //make sure we aren't returning an invalid case id
|
|
}
|
|
$db->CompleteTrans();
|
|
|
|
return $case_id;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the token based on the case id
|
|
*
|
|
* @param int $case_id The case id
|
|
*
|
|
* @return string|bool The token otherwise false if case doesn't exist
|
|
* @author Adam Zammit <adam.zammit@acspri.org.au>
|
|
* @since 2013-02-25
|
|
*/
|
|
function get_token($case_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT token
|
|
FROM `case`
|
|
WHERE case_id = $case_id";
|
|
|
|
$token = $db->GetOne($sql);
|
|
|
|
if (empty($token)) return FALSE;
|
|
|
|
return $token;
|
|
}
|
|
|
|
/**
|
|
* Return the phone number of a call
|
|
*
|
|
* @param int $call_id The call id
|
|
* @return bool|string The number to call otherwise False if cannot find
|
|
*
|
|
*/
|
|
function get_call_number($call_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT p.phone
|
|
FROM `call` as c
|
|
JOIN (contact_phone as p) on (p.contact_phone_id = c.contact_phone_id)
|
|
WHERE c.call_id = '$call_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if(!empty($rs))
|
|
return $rs['phone'];
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set the extension status in the database
|
|
*
|
|
* @param int $operator_id The queXS Operator ID
|
|
* @param bool the extension status (false for offline, true for online)
|
|
*
|
|
*/
|
|
function set_extension_status($operator_id,$online = true)
|
|
{
|
|
global $db;
|
|
|
|
$s = 0;
|
|
|
|
if ($online) $s = 1;
|
|
|
|
$sql = "UPDATE `extension`
|
|
SET status = '$s'
|
|
WHERE current_operator_id = '$operator_id'";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
/**
|
|
* Return the extension status from the database
|
|
*
|
|
* @param int $operator_id The queXS Operator ID
|
|
* @return bool the extension status (false for offline, true for online)
|
|
*
|
|
*/
|
|
function get_extension_status($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT e.status
|
|
FROM `extension` as e
|
|
WHERE e.current_operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
if (!empty($rs) && $rs['status'] == 1 ) return true;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return the extension password of an operator
|
|
*
|
|
* @param int $operator_id The queXS Operator ID
|
|
* @return string|bool the extension password or false if cannot find
|
|
*
|
|
*/
|
|
function get_extension_password($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT e.password
|
|
FROM `extension` as e
|
|
WHERE e.current_operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
if (!empty($rs) && isset($rs['password'])) return $rs['password'];
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return the extension of an operator
|
|
*
|
|
* @param int $operator_id The queXS Operator ID
|
|
* @return string|bool the extension or false if cannot find
|
|
*
|
|
*/
|
|
function get_extension($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT e.extension
|
|
FROM `extension` as e
|
|
WHERE e.current_operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
if (!empty($rs) && isset($rs['extension'])) return $rs['extension'];
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the current operator id based on PHP_AUTH_USER
|
|
*
|
|
* @return bool|int False if none otherwise the operator id
|
|
*
|
|
*/
|
|
function get_operator_id()
|
|
{
|
|
if (!isset($_SERVER['PHP_AUTH_USER']))
|
|
{
|
|
print "<p>" . T_("ERROR: You do not have server side authentication enabled therefore queXS cannot determine which user is accessing the system.") . "</p>";
|
|
return false;
|
|
}
|
|
|
|
global $db;
|
|
|
|
$sql = "SELECT operator_id
|
|
FROM operator
|
|
WHERE username = " . $db->qstr($_SERVER['PHP_AUTH_USER']) . "
|
|
AND enabled = 1";
|
|
|
|
$o = $db->GetRow($sql);
|
|
|
|
if (empty($o)) return false;
|
|
|
|
return $o['operator_id'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the time in UTC from the database
|
|
*
|
|
* @return string The date and time in format: YYYY-MM-DD HH:MM:SS
|
|
*
|
|
*/
|
|
function get_db_time()
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT CONVERT_TZ(NOW(),'System','UTC') as time";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
return $rs['time'];
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the time for the operator
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @param string $format Defaults to: YYYY-MM-DD HH:MM:SS, see {@link http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_date-format MySQL Date format}
|
|
* @return string The date and time in the given format
|
|
*
|
|
*/
|
|
function get_operator_time($operator_id,$format = "%Y-%m-%d %H:%i:%S")
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT DATE_FORMAT(CONVERT_TZ(NOW(),'System', Time_zone_name),'$format') as time
|
|
FROM operator
|
|
WHERE operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
return $rs['time'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the time for the respondent
|
|
*
|
|
* @param int $respondent_id The respondent id
|
|
* @param string $format Defaults to: YYYY-MM-DD HH:MM:SS, see {@link http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_date-format MySQL Date format}
|
|
* @return string The date and time in the given format
|
|
*
|
|
*/
|
|
function get_respondent_time($respondent_id,$format="%Y-%m-%d %H:%i:%S")
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT DATE_FORMAT(CONVERT_TZ(NOW(),'System', Time_zone_name),'$format') as time
|
|
FROM respondent
|
|
WHERE respondent_id = '$respondent_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
return $rs['time'];
|
|
}
|
|
|
|
/**
|
|
* Return the current questionnaire assigned to the operator
|
|
* false if none
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return bool|int False if none otherwise the questionnare id
|
|
*
|
|
*/
|
|
function get_questionnaire_id($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT questionnaire_id
|
|
FROM `case` as c
|
|
WHERE c.current_operator_id = '$operator_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if (empty($rs)) return false;
|
|
|
|
return $rs['questionnaire_id'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the id of the shift the operator is currently on
|
|
*
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return bool|int False if none otherwise the shift id
|
|
*
|
|
*/
|
|
function is_on_shift($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
|
|
$shift_id = false;
|
|
|
|
if ($case_id)
|
|
{
|
|
$sql = "SELECT s.shift_id
|
|
FROM `case` as c, `shift` as s
|
|
WHERE c.case_id = '$case_id'
|
|
AND c.questionnaire_id = s.questionnaire_id
|
|
AND s.`start` <= CONVERT_TZ(NOW(),'System','UTC')
|
|
AND s.`end` >= CONVERT_TZ(NOW(),'System','UTC')";
|
|
|
|
$row = $db->GetRow($sql);
|
|
|
|
if (!empty($row))
|
|
$shift_id = $row['shift_id'];
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in is_on_shift"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $shift_id;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the state of a call if the operator is currently on a call
|
|
* (see the call_state table for details)
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return bool|int False if none otherwise the call state id
|
|
*
|
|
*/
|
|
function is_on_call($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
|
|
$call_state_id = false;
|
|
|
|
if ($case_id)
|
|
{
|
|
$ca = get_call_attempt($operator_id,false);
|
|
|
|
if ($ca)
|
|
{
|
|
$sql = "SELECT call_id,state
|
|
FROM `call`
|
|
WHERE case_id = '$case_id'
|
|
AND operator_id = '$operator_id'
|
|
AND call_attempt_id = '$ca'
|
|
AND outcome_id = '0'";
|
|
|
|
$row = $db->GetRow($sql);
|
|
|
|
if (!empty($row))
|
|
$call_state_id = $row['state'];
|
|
}
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in is_on_call"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $call_state_id;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return true if the operator is currently on a call attempt
|
|
* A call attempt is a set of calls within a "session"
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return bool True if on a call attempt otherwise false
|
|
*
|
|
*/
|
|
function is_on_call_attempt($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
|
|
$return = false;
|
|
|
|
if ($case_id)
|
|
{
|
|
$sql = "SELECT call_attempt_id
|
|
FROM `call_attempt`
|
|
WHERE case_id = '$case_id'
|
|
AND operator_id = '$operator_id'
|
|
AND end IS NULL";
|
|
|
|
$row = $db->GetRow($sql);
|
|
|
|
if (!empty($row))
|
|
$return = true;
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in is_on_call_attempt"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $return;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Get the current call if on a call attempt
|
|
* If no call, create one
|
|
*
|
|
* @param int $operator_id The operator
|
|
* @param string|int $respondent_id The respondent
|
|
* @param string|int $contact_phone_id The number to contact the respondent on
|
|
* @param bool $create Whether or not to create a call
|
|
* @return bool|int False if no call exists or can be created otherwise the call_id
|
|
*
|
|
*/
|
|
function get_call($operator_id,$respondent_id = "",$contact_phone_id = "",$create = false)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
$ca = get_call_attempt($operator_id,false);
|
|
|
|
$id = false;
|
|
|
|
if ($case_id && $ca)
|
|
{
|
|
$sql = "SELECT call_id
|
|
FROM `call`
|
|
WHERE case_id = '$case_id'
|
|
AND operator_id = '$operator_id'
|
|
AND call_attempt_id = '$ca'
|
|
AND outcome_id = '0'";
|
|
|
|
$row = $db->GetRow($sql);
|
|
if (empty($row))
|
|
{
|
|
if (!empty($respondent_id) && !empty($contact_phone_id) && $create)
|
|
{
|
|
$sql = "INSERT INTO `call` (call_id,operator_id,case_id,call_attempt_id,start,end,respondent_id,contact_phone_id,outcome_id,state)
|
|
VALUES (NULL,'$operator_id','$case_id','$ca',CONVERT_TZ(NOW(),'System','UTC'),NULL,'$respondent_id','$contact_phone_id','0','1')";
|
|
$db->Execute($sql);
|
|
$id = $db->Insert_Id();
|
|
|
|
//If respondent selection is enabled, add token to RS Limesurvey database
|
|
$lime_rsid = is_respondent_selection($operator_id);
|
|
|
|
if ($lime_rsid !== true && $lime_rsid > 0 && !$db->HasFailedTrans())
|
|
//if the transaction hasn't failed and Limesurvey RS is enabled
|
|
{
|
|
$sql = "INSERT INTO ".LIME_PREFIX."tokens_$lime_rsid (tid,firstname,lastname,email,token,language,sent,completed,mpid)
|
|
VALUES (NULL,'','','',$id,'en','N','N',NULL)";
|
|
|
|
//Insert the token as the call_id
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
$id = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$id = $row['call_id'];
|
|
}
|
|
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in get_call"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $id;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the complete URL for the Limesurvey questionnaire of respondent selection
|
|
* If no call available, return an error screen
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @param bool $escape Whether to escape the ampersands default true
|
|
* @param bool $interface2 Whether we are using the alternate interface
|
|
* @return string The URL of the LimeSurvey questionnaire, or the URL of an error screen if none available
|
|
*
|
|
*/
|
|
function get_respondentselection_url($operator_id,$escape = true,$interface2 = false)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$url = "nocallavailable.php";
|
|
|
|
$call_id = get_call($operator_id);
|
|
|
|
$amp = "&";
|
|
if (!$escape) $amp = "&";
|
|
|
|
if ($call_id)
|
|
{
|
|
$sid = get_limesurvey_id($operator_id,true); //true for RS
|
|
if ($sid != false && !empty($sid) && $sid != 'NULL')
|
|
$url = LIME_URL . "index.php?interviewer=interviewer" . $amp . "loadall=reload" . $amp . "sid=$sid" . $amp . "token=$call_id" . $amp . "lang=" . DEFAULT_LOCALE;
|
|
else
|
|
{
|
|
if (is_respondent_selection($operator_id) === false)
|
|
{
|
|
$url = get_limesurvey_url($operator_id);
|
|
if (!$escape)
|
|
$url = str_replace("&","&",$url);
|
|
}
|
|
else
|
|
{
|
|
if ($interface2)
|
|
$url = 'rs_intro_interface2.php';
|
|
else
|
|
$url = 'rs_intro.php';
|
|
}
|
|
}
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in get_limesurvey_url"; exit; }
|
|
$db->CompleteTrans();
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Get the complete URL for the Limesurvey questionnaire
|
|
* If no case available, return an error screen
|
|
*
|
|
* @param int $operator_id The operator id
|
|
* @return string The URL of the LimeSurvey questionnaire, or the URL of an error screen if none available
|
|
*
|
|
*/
|
|
function get_limesurvey_url($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$url = "nocaseavailable.php";
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
|
|
if ($case_id)
|
|
{
|
|
$sql = "SELECT token
|
|
FROM `case`
|
|
WHERE case_id = $case_id";
|
|
|
|
$token = $db->GetOne($sql);
|
|
|
|
$sid = get_limesurvey_id($operator_id);
|
|
$url = LIME_URL . "index.php?interviewer=interviewer&loadall=reload&sid=$sid&token=$token&lang=" . DEFAULT_LOCALE;
|
|
$questionnaire_id = get_questionnaire_id($operator_id);
|
|
|
|
//get prefills
|
|
$sql = "SELECT lime_sgqa,value
|
|
FROM questionnaire_prefill
|
|
WHERE questionnaire_id = '$questionnaire_id'";
|
|
|
|
$pf = $db->GetAll($sql);
|
|
|
|
if (!empty($pf))
|
|
{
|
|
foreach ($pf as $p)
|
|
$url .= "&" . $p['lime_sgqa'] . "=" . template_replace($p['value'],$operator_id,$case_id);
|
|
}
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in get_limesurvey_url"; exit; }
|
|
$db->CompleteTrans();
|
|
|
|
return $url;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the appointment id if this call attempt is on an appointment
|
|
*
|
|
* @param int $call_attempt_id The current call attempt id
|
|
* @return bool|int False if no appointment otherwise the appointment id
|
|
*
|
|
*/
|
|
function is_on_appointment($call_attempt_id)
|
|
{
|
|
global $db;
|
|
//return the appointment id if this call attempt is on an appointment
|
|
|
|
$sql = "SELECT a.appointment_id
|
|
FROM call_attempt as ca
|
|
LEFT JOIN appointment as a on (a.case_id = ca.case_id and (ca.start >= a.start and ca.start <= a.end) and a.completed_call_id is NULL)
|
|
WHERE ca.call_attempt_id = '$call_attempt_id'
|
|
";
|
|
|
|
$a = $db->GetRow($sql);
|
|
|
|
if (empty($a) || empty($a['appointment_id']))
|
|
return false;
|
|
else
|
|
return $a['appointment_id'];
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Whether we should leave a message on the answering machine or not
|
|
*
|
|
* @param int $case_id The current case id
|
|
* @return bool True if we should leave a message on the machine, false otherwise
|
|
*
|
|
*/
|
|
function leave_message($case_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT (SELECT count(*) as count FROM `call` WHERE case_id = '$case_id' AND outcome_id = '23') as messages_left, qs.answering_machine_messages
|
|
FROM `questionnaire_sample` as qs, `case` as c, `sample` as s
|
|
WHERE c.case_id = '$case_id'
|
|
AND qs.questionnaire_id = c.questionnaire_id
|
|
AND s.sample_id = c.sample_id
|
|
AND qs.sample_import_id = s.import_id";
|
|
|
|
$a = $db->GetRow($sql);
|
|
|
|
if (!empty($a))
|
|
{
|
|
//if ($a['answering_machine_messages'] == 0) return true; //unlimited
|
|
if ($a['messages_left'] < $a['answering_machine_messages']) return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Return the appointment id if this call attempt is calling
|
|
* where an appointment was made and not kept
|
|
*
|
|
* @param int $call_attempt_id The current call attempt id
|
|
* @return bool|int False if no appointment otherwise the appointment id
|
|
*
|
|
*/
|
|
function missed_appointment($call_attempt_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT a.appointment_id
|
|
FROM call_attempt as ca
|
|
LEFT JOIN appointment as a on (a.case_id = ca.case_id and ca.start >= a.end and a.completed_call_id is NULL)
|
|
WHERE ca.call_attempt_id = '$call_attempt_id'";
|
|
|
|
$a = $db->GetRow($sql);
|
|
|
|
if (empty($a) || empty($a['appointment_id']))
|
|
return false;
|
|
else
|
|
return $a['appointment_id'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Update the quota table (sample wide)
|
|
*
|
|
* @param int $questionnaire_id The questionnaire ID to update
|
|
*/
|
|
function update_quota($questionnaire_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT questionnaire_sample_quota_id,q.questionnaire_id,sample_import_id,lime_sgqa,value,comparison,completions,quota_reached,q.lime_sid
|
|
FROM questionnaire_sample_quota as qsq, questionnaire as q
|
|
WHERE qsq.questionnaire_id = '$questionnaire_id'
|
|
AND q.questionnaire_id = '$questionnaire_id'
|
|
and qsq.quota_reached != '1'";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
if (isset($rs) && !empty($rs))
|
|
{
|
|
//include limesurvey functions
|
|
include_once(dirname(__FILE__).'/functions.limesurvey.php');
|
|
|
|
//update all quotas for this questionnaire
|
|
foreach($rs as $r)
|
|
{
|
|
$completions = limesurvey_quota_completions($r['lime_sgqa'],$r['lime_sid'],$r['questionnaire_id'],$r['sample_import_id'],$r['value'],$r['comparison']);
|
|
|
|
if ($completions >= $r['completions'])
|
|
{
|
|
//set quota to reached
|
|
$sql = "UPDATE questionnaire_sample_quota
|
|
SET quota_reached = '1'
|
|
WHERE questionnaire_sample_quota_id = {$r['questionnaire_sample_quota_id']}";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* "Open" a row quota (allow to access)
|
|
*
|
|
* @param int $questionnaire_sample_quota_row_id The qsqri
|
|
*/
|
|
function open_row_quota($questionnaire_sample_quota_row_id,$delete = true)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$sql = "SELECT questionnaire_id
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_sample_quota_row_id = '$questionnaire_sample_quota_row_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if ($delete)
|
|
{
|
|
$sql = "DELETE FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_sample_quota_row_id = '$questionnaire_sample_quota_row_id'";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
$sql = "DELETE FROM questionnaire_sample_quota_row_exclude
|
|
WHERE questionnaire_sample_quota_row_id = '$questionnaire_sample_quota_row_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
if (!empty($rs))
|
|
update_quota_priorities($rs['questionnaire_id']);
|
|
else
|
|
die("error in open_row_quota");
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
/**
|
|
* "Close" a row quota (set to completed)
|
|
*
|
|
*
|
|
*/
|
|
function close_row_quota($questionnaire_sample_quota_row_id,$update = true)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
//only insert where we have to
|
|
$sql = "SELECT count(*) as c
|
|
FROM questionnaire_sample_quota_row_exclude
|
|
WHERE questionnaire_sample_quota_row_id = '$questionnaire_sample_quota_row_id'";
|
|
|
|
$coun = $db->GetRow($sql);
|
|
|
|
$sql = "SELECT questionnaire_id
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_sample_quota_row_id = '$questionnaire_sample_quota_row_id'";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if (isset($coun['c']) && $coun['c'] == 0)
|
|
{
|
|
//store list of sample records to exclude
|
|
$sql = "INSERT INTO questionnaire_sample_quota_row_exclude (questionnaire_sample_quota_row_id,questionnaire_id,sample_id)
|
|
SELECT $questionnaire_sample_quota_row_id,qs.questionnaire_id,s.sample_id
|
|
FROM sample as s, sample_var as sv, questionnaire_sample_quota_row as qs
|
|
WHERE s.import_id = qs.sample_import_id
|
|
AND qs.questionnaire_sample_quota_row_id = $questionnaire_sample_quota_row_id
|
|
AND s.sample_id = sv.sample_id
|
|
AND sv.var = qs.exclude_var
|
|
AND qs.exclude_val LIKE sv.val";
|
|
|
|
$db->Execute($sql);
|
|
|
|
if ($update)
|
|
{
|
|
if (!empty($rs))
|
|
update_quota_priorities($rs['questionnaire_id']);
|
|
else
|
|
die("error in close_row_quota");
|
|
}
|
|
}
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
|
|
/**
|
|
* Copy row quotas from one sample to another
|
|
* Set quota_reached to 0 by default
|
|
*
|
|
* @param int $questionnaire_id
|
|
* @param int $sample_import_id
|
|
* @param int $copy_sample_import_id The sample_import_id to copy to
|
|
*/
|
|
function copy_row_quota($questionnaire_id,$sample_import_id,$copy_sample_import_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
//Set quota_reached to 0 always
|
|
|
|
$sql = "INSERT INTO questionnaire_sample_quota_row (questionnaire_id,sample_import_id,lime_sgqa,value,comparison,completions,exclude_var,exclude_val,quota_reached,description)
|
|
SELECT questionnaire_id, $copy_sample_import_id, lime_sgqa,value,comparison,completions,exclude_var,exclude_val,0,description
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_id = '$questionnaire_id'
|
|
AND sample_import_id = '$sample_import_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
update_quotas($questionnaire_id);
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
/**
|
|
* Copy row quotas from one sample to another with blocking
|
|
* Set quota_reached to 0 by default
|
|
*
|
|
* @param int $questionnaire_id
|
|
* @param int $sample_import_id
|
|
* @param int $copy_sample_import_id The sample_import_id to copy to
|
|
*/
|
|
function copy_row_quota_with_blocking($questionnaire_id,$sample_import_id,$copy_sample_import_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
//Set quota_reached to 0 always
|
|
|
|
$sql = "INSERT INTO questionnaire_sample_quota_row (questionnaire_id,sample_import_id,lime_sgqa,value,comparison,completions,exclude_var,exclude_val,quota_reached,description)
|
|
SELECT questionnaire_id, $copy_sample_import_id, lime_sgqa,value,comparison,completions,exclude_var,exclude_val,quota_reached,description
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_id = '$questionnaire_id'
|
|
AND sample_import_id = '$sample_import_id'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
|
|
update_quotas($questionnaire_id);
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
/**
|
|
* Copy row quotas from one sample to another and adjust completion number appropriately to completed in the sample.
|
|
*
|
|
* @param int $questionnaire_id
|
|
* @param int $sample_import_id
|
|
* @param int $copy_sample_import_id The sample_import_id to copy to
|
|
*/
|
|
function copy_row_quota_with_adjusting($questionnaire_id,$sample_import_id,$copy_sample_import_id)
|
|
{
|
|
global $db;
|
|
|
|
// Copy quotas (defalt Quexs function)
|
|
copy_row_quota_with_blocking($questionnaire_id,$sample_import_id,$copy_sample_import_id);
|
|
|
|
$db->StartTrans();
|
|
|
|
// Select quotas from the old sample rows and calculate
|
|
$sql = "SELECT questionnaire_sample_quota_row_id,q.questionnaire_id,sample_import_id,lime_sgqa,value,comparison,completions,quota_reached,q.lime_sid,qsq.exclude_var,qsq.exclude_val
|
|
FROM questionnaire_sample_quota_row as qsq, questionnaire as q
|
|
WHERE qsq.questionnaire_id = '$questionnaire_id'
|
|
AND q.questionnaire_id = '$questionnaire_id'
|
|
#AND qsq.quota_reached != '1'
|
|
AND qsq.lime_sgqa != -1
|
|
AND sample_import_id='".$sample_import_id."'
|
|
";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
if (isset($rs) && !empty($rs))
|
|
{
|
|
//include limesurvey functions
|
|
include_once(dirname(__FILE__).'/functions.limesurvey.php');
|
|
|
|
//update all row quotas for this questionnaire
|
|
foreach($rs as $r)
|
|
{
|
|
$completions = limesurvey_quota_completions($r['lime_sgqa'],$r['lime_sid'],$r['questionnaire_id'],$r['sample_import_id'],$r['value'],$r['comparison']);
|
|
if ($completions > 0)
|
|
{
|
|
//Update adjusting the completion number
|
|
$sql = "UPDATE questionnaire_sample_quota_row
|
|
SET completions = IF(completions>".$completions.",(completions-".$completions."),0), quota_reached = IF(quota_reached = 0,IF(completions=0,1,0),1)
|
|
WHERE questionnaire_id = '".$questionnaire_id."'
|
|
AND sample_import_id='".$copy_sample_import_id."'
|
|
AND lime_sgqa='".$r['lime_sgqa']."'
|
|
AND value='".$r['value']."'
|
|
AND comparison='".$r['comparison']."'";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the row quota table
|
|
*
|
|
* @param int $questionnaire_id The questionnaire ID to update
|
|
* @param int|bool $case_id The case id if known to limit the scope of the search
|
|
*/
|
|
function update_row_quota($questionnaire_id,$case_id = false)
|
|
{
|
|
global $db;
|
|
|
|
$update = false; //whether to update priorities (only if changed)
|
|
|
|
$db->StartTrans();
|
|
|
|
$sql = "SELECT questionnaire_sample_quota_row_id,q.questionnaire_id,sample_import_id,lime_sgqa,value,comparison,completions,quota_reached,q.lime_sid,qsq.exclude_var,qsq.exclude_val,qsq.current_completions,qsq.priority,qsq.autoprioritise
|
|
FROM questionnaire_sample_quota_row as qsq, questionnaire as q
|
|
WHERE qsq.questionnaire_id = '$questionnaire_id'
|
|
AND q.questionnaire_id = '$questionnaire_id'
|
|
AND qsq.quota_reached != '1'
|
|
AND qsq.lime_sgqa != -1";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
if (isset($rs) && !empty($rs))
|
|
{
|
|
//include limesurvey functions
|
|
include_once(dirname(__FILE__).'/functions.limesurvey.php');
|
|
|
|
//update all row quotas for this questionnaire
|
|
foreach($rs as $r)
|
|
{
|
|
//whether a completion was changed for this quota
|
|
$updatequota = false;
|
|
|
|
//if a case_Id is specified, we can just check if this case matches
|
|
//the quota criteria, and if so, increment the quota completions counter
|
|
if ($case_id != false)
|
|
{
|
|
if ($r['lime_sgqa'] == -2)
|
|
$match = limesurvey_quota_replicate_match($r['lime_sid'],$case_id,$r['exclude_val'],$r['exclude_var'],$r['sample_import_id']);
|
|
else
|
|
$match = limesurvey_quota_match($r['lime_sgqa'],$r['lime_sid'],$case_id,$r['value'],$r['comparison'],$r['sample_import_id']);
|
|
|
|
if ($match == 1)
|
|
{
|
|
//increment completions
|
|
$sql = "SELECT (current_completions + 1) as c
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_sample_quota_row_id = {$r['questionnaire_sample_quota_row_id']}";
|
|
$cc = $db->GetRow($sql);
|
|
|
|
$completions = $cc['c'];
|
|
|
|
$updatequota = true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if ($r['lime_sgqa'] == -2)
|
|
$completions = limesurvey_quota_replicate_completions($r['lime_sid'],$r['questionnaire_id'],$r['sample_import_id'],$r['exclude_val'],$r['exclude_var']);
|
|
else
|
|
$completions = limesurvey_quota_completions($r['lime_sgqa'],$r['lime_sid'],$r['questionnaire_id'],$r['sample_import_id'],$r['value'],$r['comparison']);
|
|
$updatequota = true;
|
|
}
|
|
|
|
if ($updatequota)
|
|
{
|
|
if ($completions >= $r['completions'])
|
|
{
|
|
//set row quota to reached
|
|
$sql = "UPDATE questionnaire_sample_quota_row
|
|
SET quota_reached = '1', current_completions = '$completions'
|
|
WHERE questionnaire_sample_quota_row_id = {$r['questionnaire_sample_quota_row_id']}";
|
|
|
|
$db->Execute($sql);
|
|
|
|
close_row_quota($r['questionnaire_sample_quota_row_id'],false); //don't update priorires just yet
|
|
$update = true;
|
|
}
|
|
else
|
|
{
|
|
$sql = "UPDATE questionnaire_sample_quota_row
|
|
SET current_completions = '$completions' ";
|
|
|
|
//If autopriority is set update it here
|
|
if ($r['autoprioritise'] == 1)
|
|
{
|
|
//priority is 100 - the percentage of completions
|
|
$pr = 100 - round(100 * ($completions / $r['completions']));
|
|
if ($pr < 0)
|
|
$pr = 0;
|
|
$sql .= ", priority = '$pr' ";
|
|
|
|
//need to update quotas now
|
|
$update = true;
|
|
}
|
|
|
|
$sql .= " WHERE questionnaire_sample_quota_row_id = {$r['questionnaire_sample_quota_row_id']}";
|
|
|
|
$db->Execute($sql);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
if ($update) update_quota_priorities($questionnaire_id);
|
|
}
|
|
|
|
$db->CompleteTrans();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* Check if any quotas apply to this questionnaire
|
|
* and if so, update them
|
|
*
|
|
* @param int $questionnaire_id The questionnaire id
|
|
* @param int $case_id The case id if specified
|
|
*
|
|
*/
|
|
function update_quotas($questionnaire_id,$case_id = false)
|
|
{
|
|
update_quota($questionnaire_id);
|
|
update_row_quota($questionnaire_id,$case_id);
|
|
}
|
|
|
|
/**
|
|
* Update quota priorities and exclusion table
|
|
*
|
|
* @param int $questionnaire_id The questionnaire_id to update priorities for
|
|
*
|
|
*/
|
|
function update_quota_priorities($questionnaire_id)
|
|
{
|
|
global $db;
|
|
|
|
//update questionnaire_sample_exclude_priority table to have records for each q and s
|
|
//set exclude == 1 where records exist in qsqre table
|
|
//placeholder to set priorities also
|
|
|
|
$db->StartTrans();
|
|
|
|
$sql = "INSERT INTO questionnaire_sample_exclude_priority (questionnaire_id,sample_id,exclude,priority)
|
|
SELECT '$questionnaire_id', s.sample_id, 0, 50
|
|
FROM sample AS s, questionnaire_sample as qs
|
|
WHERE qs.questionnaire_id = '$questionnaire_id'
|
|
AND s.import_id = qs.sample_import_id
|
|
ON DUPLICATE KEY UPDATE exclude = 0, priority = 50";
|
|
|
|
$db->Execute($sql);
|
|
|
|
if ($db->HasFailedTrans()) die ($sql);
|
|
|
|
//Update the priority record
|
|
|
|
//Select all quota rows that are open, and have a priority != 50
|
|
$sql = "SELECT questionnaire_sample_quota_row_id, priority
|
|
FROM questionnaire_sample_quota_row
|
|
WHERE questionnaire_id = '$questionnaire_id'
|
|
AND quota_reached = 0
|
|
AND priority != 50
|
|
ORDER BY priority ASC";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
if ($db->HasFailedTrans()) die ($sql);
|
|
|
|
if (!empty($rs)) foreach ($rs as $r)
|
|
{
|
|
$qsqri = $r['questionnaire_sample_quota_row_id'];
|
|
$priority = $r['priority'];
|
|
|
|
//find all cases that match this quota, and update it to the new priority
|
|
$sql = "UPDATE sample as s, sample_var as sv, questionnaire_sample_quota_row as qs, questionnaire_sample_exclude_priority as qsep
|
|
SET qsep.priority = '$priority'
|
|
WHERE s.import_id = qs.sample_import_id
|
|
AND qs.questionnaire_sample_quota_row_id = '$qsqri'
|
|
AND s.sample_id = sv.sample_id
|
|
AND sv.var = qs.exclude_var
|
|
AND qs.exclude_val LIKE sv.val
|
|
AND qsep.questionnaire_id = qs.questionnaire_id
|
|
AND qsep.sample_id = s.sample_id";
|
|
|
|
|
|
$db->Execute($sql);
|
|
|
|
|
|
if ($db->HasFailedTrans()) die ($sql);
|
|
}
|
|
|
|
|
|
//Update the exclusion record to be 1 where exists in the qsqre table
|
|
|
|
$sql = "SELECT questionnaire_id,sample_id
|
|
FROM questionnaire_sample_quota_row_exclude
|
|
WHERE questionnaire_id = '$questionnaire_id'
|
|
GROUP BY questionnaire_id,sample_id";
|
|
|
|
$rs = $db->GetAll($sql);
|
|
|
|
if ($db->HasFailedTrans()) die ($sql);
|
|
|
|
foreach($rs as $r)
|
|
{
|
|
$q = $r['questionnaire_id'];
|
|
$s = $r['sample_id'];
|
|
|
|
$sql = "UPDATE questionnaire_sample_exclude_priority
|
|
SET exclude = 1
|
|
WHERE questionnaire_id = '$q'
|
|
AND sample_id = '$s'";
|
|
|
|
$db->Execute($sql);
|
|
|
|
|
|
if ($db->HasFailedTrans()) die ($sql);
|
|
}
|
|
|
|
$db->CompleteTrans();
|
|
}
|
|
|
|
|
|
/**
|
|
* End the current case
|
|
*
|
|
* @param int $operator_id The operator to end the case for
|
|
*
|
|
* @see get_case()
|
|
*/
|
|
function end_case($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
$questionnaire_id = get_questionnaire_id($operator_id);
|
|
|
|
$return = false;
|
|
|
|
if ($case_id)
|
|
{
|
|
//End all calls (with not attempted or worked if there is a call);
|
|
end_call($operator_id,1);
|
|
|
|
//Make sure to end call attempts
|
|
end_call_attempt($operator_id);
|
|
|
|
//determine current final outcome code
|
|
//Look over all calls, for each phone number that is to be tried again
|
|
//Calculate outcome based on
|
|
//If no phone number is to be tried again, use the outcome from the last call
|
|
//If one phone number is to be tried again, use: "Differences in Response Rates using Most recent versus Final dispositions in Telephone Surveys" by Christopher McCarty
|
|
//
|
|
|
|
//Look for any calls where none should be tried again (this should be a final outcome)
|
|
$sql = "SELECT c.call_id, c.outcome_id
|
|
FROM `call` as c, `outcome` as o
|
|
WHERE c.case_id = '$case_id'
|
|
AND c.outcome_id = o.outcome_id
|
|
AND o.tryanother = 0
|
|
AND (o.outcome_type_id = 4)
|
|
ORDER BY c.call_id DESC
|
|
LIMIT 1";
|
|
|
|
$a = $db->GetRow($sql);
|
|
|
|
if (empty($a))
|
|
{
|
|
|
|
$sql = "SELECT c.*
|
|
FROM contact_phone AS c
|
|
LEFT JOIN (
|
|
SELECT contact_phone.contact_phone_id
|
|
FROM contact_phone
|
|
LEFT JOIN `call` ON ( call.contact_phone_id = contact_phone.contact_phone_id )
|
|
LEFT JOIN outcome ON ( call.outcome_id = outcome.outcome_id )
|
|
WHERE contact_phone.case_id = '$case_id'
|
|
AND outcome.tryagain =0
|
|
) AS l ON l.contact_phone_id = c.contact_phone_id
|
|
WHERE c.case_id = '$case_id'
|
|
AND l.contact_phone_id IS NULL";
|
|
|
|
$r = $db->GetAll($sql);
|
|
|
|
//$r contains one row for each phone number that is to be tried again
|
|
if (!empty($r))
|
|
$count = count($r);
|
|
else
|
|
$count = 0;
|
|
|
|
$outcome = 1; //default outcome is 1 - not attempted
|
|
|
|
//last call
|
|
$sql = "SELECT call_id,outcome_id
|
|
FROM `call`
|
|
WHERE case_id = '$case_id'
|
|
ORDER BY call_id DESC
|
|
LIMIT 1";
|
|
|
|
$l = $db->GetRow($sql);
|
|
|
|
$lastcall = 0;
|
|
if (!empty($l))
|
|
$lastcall = $l['call_id'];
|
|
|
|
|
|
if ($count == 0) //no numbers to be tried again, get last outcome or 1
|
|
{
|
|
//last call
|
|
$sql = "SELECT c.outcome_id as outcome_id
|
|
FROM `call` as c
|
|
JOIN outcome AS o ON ( c.outcome_id = o.outcome_id)
|
|
WHERE c.case_id = '$case_id'
|
|
AND o.outcome_id != 18
|
|
ORDER BY c.call_id DESC
|
|
LIMIT 1";
|
|
|
|
$t = $db->GetRow($sql);
|
|
|
|
if (!empty($t))
|
|
{
|
|
$outcome = $t['outcome_id'];
|
|
}
|
|
}
|
|
else if ($count >= 1) //one or more numbers to be tried again - first code as eligible if ever eligible...
|
|
{
|
|
//$r[0]['contact_phone_id'];
|
|
//code as eligible if ever eligible, or if referred to the supervisor, code as that if last call
|
|
$sql = "SELECT c.outcome_id as outcome_id
|
|
FROM `call` as c
|
|
JOIN outcome AS o ON ( c.outcome_id = o.outcome_id AND (o.eligible = 1 OR o.outcome_type_id = 2 OR o.outcome_type_id = 1) )
|
|
WHERE c.case_id = '$case_id'
|
|
ORDER BY c.call_id DESC";
|
|
|
|
$t = $db->GetRow($sql);
|
|
|
|
if (!empty($t))
|
|
$outcome = $t['outcome_id'];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//the last call is the call with the final otucome
|
|
$outcome = $a['outcome_id'];
|
|
$lastcall = $a['call_id'];
|
|
|
|
//if the outcome is complete, then update the quota's for this questionnaire (if any)
|
|
if ($outcome == 10)
|
|
update_quotas($questionnaire_id,$case_id);
|
|
}
|
|
|
|
$sql = "UPDATE `case`
|
|
SET current_operator_id = NULL, current_call_id = NULL, sortorder = NULL, current_outcome_id = '$outcome', last_call_id = '$lastcall'
|
|
WHERE case_id = '$case_id'";
|
|
|
|
$o = $db->Execute($sql);
|
|
|
|
$return = true;
|
|
}
|
|
else
|
|
$return = false;
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in end_case"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $return;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Outcome description
|
|
*
|
|
* @param int $outcome_id The outcome id
|
|
* @return string The description of the outcome
|
|
*/
|
|
function outcome_description($outcome_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT description
|
|
FROM outcome
|
|
WHERE outcome_id = '$outcome_id'";
|
|
|
|
$r = $db->CacheGetRow($sql);
|
|
|
|
if (!empty($r))
|
|
return T_($r['description']);
|
|
else
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* End the current call attempt
|
|
*
|
|
* @param int $operator_id The operator
|
|
*
|
|
* @see get_call_attempt()
|
|
*/
|
|
function end_call_attempt($operator_id)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$return = false;
|
|
|
|
$ca = get_call_attempt($operator_id,false);
|
|
|
|
if ($ca)
|
|
{
|
|
$sql = "UPDATE `call_attempt`
|
|
SET end = CONVERT_TZ(NOW(),'System','UTC')
|
|
WHERE call_attempt_id = '$ca'";
|
|
|
|
$o = $db->Execute($sql);
|
|
|
|
$return = true;
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in end_call_attempt"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $return;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return the respondent of the given call attempt
|
|
*
|
|
* @param int $call_attempt_id The call attempt
|
|
* @return bool|int False if no call attempt/respondent otherwise the respondent_id
|
|
*
|
|
*/
|
|
function get_respondent_id($call_attempt_id)
|
|
{
|
|
global $db;
|
|
|
|
$sql = "SELECT respondent_id
|
|
FROM call_attempt
|
|
WHERE call_attempt_id = '$call_attempt_id'";
|
|
|
|
$row = $db->GetRow($sql);
|
|
|
|
if (!empty($row))
|
|
return $row['respondent_id'];
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* Return the call attempt of the given operator
|
|
*
|
|
* @param int $operator_id The oeprator
|
|
* @param bool $create If true, will create a call attempt if none exists
|
|
* @return bool|int False if no case otherwise the call_attempt_id
|
|
*
|
|
*/
|
|
function get_call_attempt($operator_id,$create = false)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$case_id = get_case_id($operator_id,false);
|
|
|
|
$id = false;
|
|
|
|
if ($case_id)
|
|
{
|
|
$sql = "SELECT call_attempt_id
|
|
FROM `call_attempt`
|
|
WHERE case_id = '$case_id'
|
|
AND operator_id = '$operator_id'
|
|
AND end IS NULL";
|
|
|
|
$row = $db->GetRow($sql);
|
|
|
|
/**
|
|
* If no call_attempt, create one
|
|
*/
|
|
if (empty($row))
|
|
{
|
|
if ($create)
|
|
{
|
|
$sql = "SELECT respondent_id
|
|
FROM respondent
|
|
WHERE case_id = '$case_id'";
|
|
|
|
$row2 = $db->GetRow($sql);
|
|
|
|
$respondent_id = 0;
|
|
if (!empty($row2)) $respondent_id = $row2['respondent_id'];
|
|
|
|
$sql = "INSERT INTO `call_attempt` (call_attempt_id,operator_id,case_id,respondent_id,start,end)
|
|
VALUES (NULL,'$operator_id','$case_id','$respondent_id',CONVERT_TZ(NOW(),'System','UTC'),NULL)";
|
|
|
|
$db->Execute($sql);
|
|
$id = $db->Insert_Id();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$id = $row['call_attempt_id'];
|
|
}
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in get_call_attempt"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $id;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* End the current call
|
|
* Store the time and outcome in the database
|
|
*
|
|
* @param int $operator_id The operator
|
|
* @param int $outcome_id The outcome to the call
|
|
* @param int $state The end state of the call 5 default
|
|
* @return bool True if database execution succeeded
|
|
*
|
|
* @todo Implement session destruction here
|
|
*
|
|
*/
|
|
function end_call($operator_id,$outcome_id,$state = 5)
|
|
{
|
|
global $db;
|
|
|
|
$db->StartTrans();
|
|
|
|
$o = false;
|
|
|
|
$ca = get_call($operator_id);
|
|
|
|
if ($ca)
|
|
{
|
|
$c = get_call_attempt($operator_id,false);
|
|
if ($c)
|
|
{
|
|
$a = is_on_appointment($c); //if we were on an appointment, complete it with this call
|
|
if ($a)
|
|
{
|
|
$sql = "UPDATE appointment
|
|
SET completed_call_id = '$ca'
|
|
WHERE appointment_id = '$a'";
|
|
$db->Execute($sql);
|
|
}
|
|
}
|
|
|
|
$sql = "UPDATE `call`
|
|
SET end = CONVERT_TZ(NOW(),'System','UTC'), outcome_id = '$outcome_id', state = '$state'
|
|
WHERE call_id = '$ca'";
|
|
|
|
$db->Execute($sql);
|
|
}
|
|
|
|
//if ($db->HasFailedTrans()) { print "FAILED in end_call"; exit; }
|
|
if ($db->CompleteTrans())
|
|
return $o;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the limesurvey "survey id" of the current questionnaire assigned to the operator
|
|
*
|
|
* @param int $operator_id The operator
|
|
* @param bool $rs If asking for respondent selection
|
|
* @return bool|int False if none found else the limesurvey sid
|
|
*
|
|
*
|
|
*/
|
|
function get_limesurvey_id($operator_id,$rs = false)
|
|
{
|
|
global $db;
|
|
|
|
if ($rs)
|
|
$sql = "SELECT q.lime_rs_sid as sid";
|
|
else
|
|
$sql = "SELECT q.lime_sid as sid";
|
|
|
|
$sql .= " FROM `case` as c, questionnaire_sample as qs, sample as s, questionnaire as q
|
|
WHERE c.current_operator_id = '$operator_id'
|
|
AND c.sample_id = s.sample_id
|
|
AND s.import_id = qs.sample_import_id
|
|
AND q.questionnaire_id = qs.questionnaire_id
|
|
AND c.questionnaire_id = q.questionnaire_id";
|
|
|
|
$rs = $db->GetRow($sql);
|
|
|
|
if (empty($rs))
|
|
return false;
|
|
|
|
return $rs['sid'];
|
|
|
|
}
|
|
|
|
/**
|
|
* Add a respondent to the case
|
|
*
|
|
* @param int $case_id The case id
|
|
* @param string $firstName The first name of the respondent
|
|
* @param string $lastName The last name of the respondent
|
|
* @param string $Time_zone_name The TimeZone in MySQL format
|
|
*
|
|
* @return int The respondent ID
|
|
*
|
|
*/
|
|
function add_respondent($case_id,$firstName,$lastName,$Time_zone_name)
|
|
{
|
|
global $db;
|
|
|
|
$case_id = $db->qstr($case_id,get_magic_quotes_gpc());
|
|
$firstName = $db->qstr($firstName,get_magic_quotes_gpc());
|
|
$lastName = $db->qstr($lastName,get_magic_quotes_gpc());
|
|
$Time_zone_name = $db->qstr($Time_zone_name,get_magic_quotes_gpc());
|
|
|
|
$sql = "INSERT INTO respondent (respondent_id,case_id,firstName,lastName,Time_zone_name)
|
|
VALUES (NULL,$case_id,$firstName,$lastName,$Time_zone_name)";
|
|
|
|
$db->Execute($sql);
|
|
|
|
return $db->Insert_ID();
|
|
|
|
}
|
|
|
|
|
|
?>
|