2
0
mirror of https://github.com/ACSPRI/queXS synced 2024-04-02 12:12:16 +00:00

Added a "system sort" process which sorts cases and the sample file from enabled questionnaires periodically (based on SYSTEM_SORT_MINUTES config directive). Enabling this may improve performance where there is a large number of interviewers, a large sample or complex quotas as the sort doesn't need to be done for each interviewer at each case.

Allow for multiple background processes to run concurrently
This commit is contained in:
azammitdcarf
2011-02-01 05:27:55 +00:00
parent 5eb15f0450
commit 1ffb93df13
7 changed files with 428 additions and 82 deletions

View File

@@ -87,7 +87,8 @@ print "<li><h3>" . T_("System settings") . "</h3>";
print "<ul><li><a href=\"?page=timezonetemplate.php\">" . T_("Set default timezone list") . "</a></li>"; print "<ul><li><a href=\"?page=timezonetemplate.php\">" . T_("Set default timezone list") . "</a></li>";
print "<li><a href=\"?page=shifttemplate.php\">" . T_("Set default shift times") . "</a></li>"; print "<li><a href=\"?page=shifttemplate.php\">" . T_("Set default shift times") . "</a></li>";
print "<li><a href=\"?page=callrestrict.php\">" . T_("Set call restriction times") . "</a></li>"; print "<li><a href=\"?page=callrestrict.php\">" . T_("Set call restriction times") . "</a></li>";
print "<li><a href=\"?page=centreinfo.php\">" . T_("Set centre information") . "</a></li></ul></li>"; print "<li><a href=\"?page=centreinfo.php\">" . T_("Set centre information") . "</a></li>";
print "<li><a href=\"?page=systemsort.php\">" . T_("Start and monitor system wide case sorting") . "</a></li></ul></li>";
if (VOIP_ENABLED) if (VOIP_ENABLED)
{ {

100
admin/systemsort.php Normal file
View File

@@ -0,0 +1,100 @@
<?
/**
* Run the system wide case sorting process and monitor it's progress
*
*
* 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@acspri.org.au>
* @copyright Australian Consortium for Social and Political Research Incorporated (ACSPRI) 2011
* @package queXS
* @subpackage admin
* @link http://www.acspri.org.au/ queXS was writen for ACSPRI
* @license http://opensource.org/licenses/gpl-2.0.php The GNU General Public License (GPL) Version 2
*
*/
/**
* Configuration file
*/
include ("../config.inc.php");
/**
* Database file
*/
include ("../db.inc.php");
/**
* Process
*/
include ("../functions/functions.process.php");
/**
* XHTML functions
*/
include("../functions/functions.xhtml.php");
if (isset($_GET['watch']))
{
//start watching process
start_process(realpath(dirname(__FILE__) . "/systemsortprocess.php"),2);
}
$p = is_process_running(2);
if ($p)
{
if (isset($_GET['kill']))
{
if ($_GET['kill'] == "force")
end_process($p);
else
kill_process($p);
}
xhtml_head(T_("Monitor system wide case sorting"),true,false,false,false,false,true);
print "<h1>" . T_("Running process:") . " $p</h1>";
if (is_process_killed($p))
{
print "<h3>" . T_("Kill signal sent: Please wait...") . "</h3>";
print "<p><a href='?kill=force'>" . T_("Process is already closed (eg. server was rebooted) - click here to confirm") . "</a></p>";
}
else
{
print "<p><a href='?kill=kill'>" . T_("Kill the running process") . "</a></p>";
}
print process_get_data($p);
}
else
{
xhtml_head(T_("Monitor system wide case sorting"));
print "<h2>" . T_("Monitor system wide case sorting") . "</h2>";
print "<p><a href='?watch=watch'>" . T_("Click here to enable and begin system wide case sorting") . "</a></p>";
print "<p>" . T_("System wide case sorting is periodically (via SYSTEM_SORT_MINUTES configuration directive) sorting cases on a system wide basis instead of finding the most appropriate case each time an operator requests a new case. This may increase performance where there are a large number of cases or complex quotas in place. If you are not experiencing any performance problems, it is not recommended to use this feature.") . "</p>";
print "<h2>" . T_("Outcome of last process run (if any)") . "</h2>";
print process_get_last_data(2);
}
xhtml_foot();
?>

194
admin/systemsortprocess.php Normal file
View File

@@ -0,0 +1,194 @@
<?
/**
* Run the system wide case sorting process
*
*
* 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@acspri.org.au>
* @copyright Australian Consortium for Social and Political Research Incorporated (ACSPRI) 2011
* @package queXS
* @subpackage admin
* @link http://www.acspri.org.au/ queXS was written for ACSPRI
* @license http://opensource.org/licenses/gpl-2.0.php The GNU General Public License (GPL) Version 2
*
*/
/**
* Configuration file
*/
include (dirname(__FILE__) . "/../config.inc.php");
/**
* Database file
*/
include (dirname(__FILE__) . "/../db.inc.php");
/**
* Process
*/
include (dirname(__FILE__) . "/../functions/functions.process.php");
/**
* Update the database with the new data from the running script
*
* @param string $buffer The data to append to the database
* @return string Return a blank string to empty the buffer
*/
function update_callback($buffer)
{
global $process_id;
process_append_data($process_id,"<p>" . $buffer . "</p>");
return ""; //empty buffer
}
/**
* Disable system sort on shutdown
*
* @author Adam Zammit <adam.zammit@acspri.org.au>
* @since 2011-01-31
*/
function disable_systemsort()
{
set_setting('systemsort',false);
}
//get the arguments from the command line (this process_id)
if ($argc != 2) exit();
$process_id = $argv[1];
//register an exit function which will tell the database we have ended
register_shutdown_function('end_process',$process_id);
register_shutdown_function('disable_systemsort');
//all output send to database instead of stdout
ob_start('update_callback',2);
print T_("Sorting cases process starting");
$sleepinterval = 10; // in seconds so we can monitor if the process has been killed
while (!is_process_killed($process_id)) //check if process killed every $sleepinterval
{
//Make sure that the system knows we are system sorting
set_setting('systemsort',true);
print date("Y-m-d H:i") . " : " . T_("Sorting cases");
$time_start = microtime(true);
$db->StartTrans();
//Sort current cases for all enabled questionnaires
$sql = "SELECT c.case_id
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, questionnaire as q, outcome as ou) on (c.questionnaire_id = q.questionnaire_id and qs.sample_import_id = s.import_id and ou.outcome_id = c.current_outcome_id and q.enabled = 1)
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)
WHERE c.current_operator_id IS NULL
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 !(si.call_restrict = 1 AND cr.day_of_week IS NULL)
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 (SELECT count(*) FROM `questionnaire_sample_quota` WHERE questionnaire_id = c.questionnaire_id AND sample_import_id = s.import_id AND quota_reached = 1) = 0
GROUP BY c.case_id
ORDER BY apn.start DESC, a.start ASC, qsep.priority DESC";
$rs = $db->GetAll($sql);
$i = 1;
foreach ($rs as $r)
{
$sql = "UPDATE `case`
SET sortorder = '$i'
WHERE case_id = '{$r['case_id']}'";
$db->Execute($sql);
$i++;
}
//Sort sample list where attached to an enabled questionnaire
$sql = "SELECT s.sample_id as sample_id,qs.questionnaire_id as questionnaire_id
FROM sample as s
JOIN (questionnaire_sample as qs, questionnaire as q, sample_import as si) on (qs.sample_import_id = s.import_id and si.sample_import_id = s.import_id and q.questionnaire_id = qs.questionnaire_id AND q.enabled = 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
GROUP BY s.sample_id,qs.questionnaire_id
ORDER BY qsep.priority DESC, rand() * qs.random_select, s.sample_id";
$rs = $db->GetAll($sql);
$i = 1;
foreach ($rs as $r)
{
$sql = "INSERT INTO questionnaire_sample_exclude_priority (questionnaire_id,sample_id,exclude,priority,sortorder)
VALUES ('{$r['questionnaire_id']}', '{$r['sample_id']}', 0, 50,'$i')
ON DUPLICATE KEY UPDATE sortorder = '$i'";
$db->Execute($sql);
$i++;
}
$result = $db->CompleteTrans();
$time_end = microtime(true);
$timer = $time_end - $time_start;
if ($result)
print T_("Completed sort") . ". " . T_("This task took") . ": $timer " . T_("seconds");
else
print T_("Failed to complete sort") . ". " . T_("This task took") . ": $timer " . T_("seconds");
for ($i = 0; $i < (SYSTEM_SORT_MINUTES * 60); $i += $sleepinterval)
{
if (is_process_killed($process_id)){break;}
sleep($sleepinterval);
}
}
disable_systemsort();
ob_get_contents();
ob_end_clean();
?>

View File

@@ -220,6 +220,11 @@ if (!defined('TAB_INFO')) define('TAB_INFO', true);
*/ */
if (!defined('HEADER_EXPANDER')) define('HEADER_EXPANDER', false); if (!defined('HEADER_EXPANDER')) define('HEADER_EXPANDER', false);
/**
* Define how many minutes between each system sort (defaults to 5 as this is a common interval for appointments)
*/
if (!defined('SYSTEM_SORT_MINUTES')) define ('SYSTEM_SORT_MINUTES',5);
/** /**
* Database configuration for queXS * Database configuration for queXS
*/ */

View File

@@ -176,12 +176,14 @@ CREATE TABLE `case` (
`current_operator_id` bigint(20) default NULL, `current_operator_id` bigint(20) default NULL,
`current_call_id` bigint(20) default NULL, `current_call_id` bigint(20) default NULL,
`current_outcome_id` int(11) NOT NULL default '1', `current_outcome_id` int(11) NOT NULL default '1',
`sortorder` int(11) default NULL,
PRIMARY KEY (`case_id`), PRIMARY KEY (`case_id`),
UNIQUE KEY `onecasepersample` (`sample_id`,`questionnaire_id`), UNIQUE KEY `onecasepersample` (`sample_id`,`questionnaire_id`),
UNIQUE KEY `current_operator_id` (`current_operator_id`), UNIQUE KEY `current_operator_id` (`current_operator_id`),
UNIQUE KEY `current_call_id` (`current_call_id`), UNIQUE KEY `current_call_id` (`current_call_id`),
KEY `sample_id` (`sample_id`), KEY `sample_id` (`sample_id`),
KEY `questionnaire_id` (`questionnaire_id`) KEY `questionnaire_id` (`questionnaire_id`),
KEY `sortorder` (`sortorder`)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
@@ -441,6 +443,7 @@ INSERT INTO `outcome_type` VALUES(5, 'Appointments');
CREATE TABLE `process` ( CREATE TABLE `process` (
`process_id` bigint(20) NOT NULL auto_increment, `process_id` bigint(20) NOT NULL auto_increment,
`type` int(11) NOT NULL default '1',
`start` datetime NOT NULL, `start` datetime NOT NULL,
`stop` datetime default NULL, `stop` datetime default NULL,
`kill` tinyint(1) NOT NULL default '0', `kill` tinyint(1) NOT NULL default '0',
@@ -522,10 +525,12 @@ CREATE TABLE `questionnaire_sample_exclude_priority` (
`sample_id` bigint(20) NOT NULL, `sample_id` bigint(20) NOT NULL,
`exclude` tinyint(1) NOT NULL default '0', `exclude` tinyint(1) NOT NULL default '0',
`priority` tinyint(3) NOT NULL default '50', `priority` tinyint(3) NOT NULL default '50',
`sortorder` int(11) default NULL,
PRIMARY KEY (`questionnaire_id`,`sample_id`), PRIMARY KEY (`questionnaire_id`,`sample_id`),
KEY `exclude` (`exclude`), KEY `exclude` (`exclude`),
KEY `priority` (`priority`), KEY `priority` (`priority`),
KEY `questionnaire_id` (`questionnaire_id`) KEY `questionnaire_id` (`questionnaire_id`),
KEY `sortorder` (`sortorder`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- --

View File

@@ -295,86 +295,122 @@ function get_case_id($operator_id, $create = false)
{ {
if ($create) if ($create)
{ {
/** $systemsort = get_setting('systemsort');
* 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
*
*
* THINGS TO ADD:
*
* @todo also option of "time of day" calls - try once in the morning/etc
* @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,s.*,apn.*,a.*,sh.*,op.*,cr.*,si.*,CONVERT_TZ(NOW(), 'System' , s.Time_zone_name) as resptime if ($systemsort)
FROM `case` as c {
LEFT JOIN `call` as a on (a.call_id = c.last_call_id) //Just make sure that this case should go to this operator (assigned to this project and skill)
JOIN (sample as s, sample_import as si) on (s.sample_id = c.sample_id and si.sample_import_id = s.import_id) $sql = "SELECT c.case_id as caseid
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 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) FROM `case` as c
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)) JOIN operator_questionnaire AS oq ON (oq.operator_id = '$operator_id' AND oq.questionnaire_id = c.questionnaire_id)
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'))) JOIN outcome as ou ON (ou.outcome_id = c.current_outcome_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)) JOIN operator_skill as os ON (os.operator_id = '$operator_id' AND os.outcome_type_id = ou.outcome_type_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) WHERE c.sortorder IS NOT NULL
LEFT JOIN questionnaire_sample_exclude_priority AS qsep ON (qsep.questionnaire_id = c.questionnaire_id AND qsep.sample_id = c.sample_id) AND c.current_operator_id IS NULL
JOIN operator_skill as os on (os.operator_id = op.operator_id and os.outcome_type_id = ou.outcome_type_id) ORDER BY c.sortorder ASC
WHERE c.current_operator_id IS NULL 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 (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 apn.start DESC, a.start ASC, qsep.priority DESC
LIMIT 1";
//apn.appointment_id contains the id of an appointment if we are calling on an appointment }
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
*
*
* THINGS TO ADD:
*
* @todo also option of "time of day" calls - try once in the morning/etc
* @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,s.*,apn.*,a.*,sh.*,op.*,cr.*,si.*,CONVERT_TZ(NOW(), 'System' , s.Time_zone_name) as resptime
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 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)
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 (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 (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 apn.start DESC, 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); $r2 = $db->GetRow($sql);
if (empty($r2)) 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
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)
LEFT JOIN `case` as c ON (c.sample_id = qsep.sample_id AND c.questionnaire_id = qsep.sample_id)
WHERE qsep.sortorder IS NOT NULL
AND c.case_id IS NULL
ORDER BY qsep.sortorder ASC
LIMIT 1";
/** }
* If no case found, we must draw the next available case from the sample else
* 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)
* /**
* * If no case found, we must draw the next available case from the sample
* Method: * only if no case due to lack of cases to call not out of shift time/etc and
* next available that has not been assigned * only draw cases that are new (Temporary outcome_type_id) - this makes sure that we are not drawing
* if none available - return false? report to operator that no one available to call at currenet settings * 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 $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 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) 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)
LEFT JOIN `case` as c on (c.sample_id = s.sample_id and c.questionnaire_id = qs.questionnaire_id) 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 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 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) 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";
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); $r3 = $db->GetRow($sql);

View File

@@ -43,15 +43,17 @@ include_once(dirname(__FILE__).'/../db.inc.php');
/** /**
* Determine if a process is already running * Determine if a process is already running
* *
* @param int $type Defaults to 1 - specify the process type (class) to search for
* @return bool|int Return false if no process already running, else return the process_id * @return bool|int Return false if no process already running, else return the process_id
*/ */
function is_process_running() function is_process_running($type = 1)
{ {
global $db; global $db;
$sql = "SELECT `process_id` $sql = "SELECT `process_id`
FROM `process` FROM `process`
WHERE `stop` IS NULL"; WHERE `stop` IS NULL
AND type = '$type'";
$rs = $db->GetRow($sql); $rs = $db->GetRow($sql);
@@ -90,23 +92,24 @@ function is_process_killed($process_id)
* Start a process * Start a process
* *
* @param string $filename The PHP file of the process to run * @param string $filename The PHP file of the process to run
* @param int $type The type (class) of process (so we can run multiple processes at the same time) defaults to 1
* @return bool|int False if we couldnt start a process, else the process id from the process table * @return bool|int False if we couldnt start a process, else the process id from the process table
* *
* @link http://www.djkaty.com/php/fork Cross platform process tutorial (this code adapted from here) * @link http://www.djkaty.com/php/fork Cross platform process tutorial (this code adapted from here)
*/ */
function start_process($filename) function start_process($filename,$type = 1)
{ {
//create a record only if no process already running //create a record only if no process already running
global $db; global $db;
$db->StartTrans(); $db->StartTrans();
$process = is_process_running(); $process = is_process_running($type);
if ($process == false) if ($process == false)
{ {
$sql = "INSERT INTO `process` (`process_id`,`start`,`stop`,`kill`,`data`) $sql = "INSERT INTO `process` (`process_id`,`type`,`start`,`stop`,`kill`,`data`)
VALUES (NULL,CONVERT_TZ(NOW(),'System','UTC'),NULL,0,'')"; VALUES (NULL,'$type',CONVERT_TZ(NOW(),'System','UTC'),NULL,0,'')";
$rs = $db->Execute($sql); $rs = $db->Execute($sql);
$args = $db->Insert_ID(); $args = $db->Insert_ID();
@@ -220,15 +223,17 @@ function process_get_data($process_id)
/** /**
* Get data from the last process run * Get data from the last process run
* *
* @param int $type The last processes class (type) defaults to 1
* @return string Data from the last process, or an empty string if not available * @return string Data from the last process, or an empty string if not available
* *
*/ */
function process_get_last_data() function process_get_last_data($type = 1)
{ {
global $db; global $db;
$sql = "SELECT `data` $sql = "SELECT `data`
FROM `process` FROM `process`
WHERE type = '$type'
ORDER BY `process_id` DESC ORDER BY `process_id` DESC
LIMIT 1"; LIMIT 1";