diff --git a/admin/index.php b/admin/index.php index e045b580..ec257857 100644 --- a/admin/index.php +++ b/admin/index.php @@ -87,7 +87,8 @@ print "
  • " . T_("System settings") . "

    "; print "
  • "; +print "
  • " . T_("Set centre information") . "
  • "; +print "
  • " . T_("Start and monitor system wide case sorting") . "
  • "; if (VOIP_ENABLED) { diff --git a/admin/systemsort.php b/admin/systemsort.php new file mode 100644 index 00000000..64410481 --- /dev/null +++ b/admin/systemsort.php @@ -0,0 +1,100 @@ + + * @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 "

    " . T_("Running process:") . " $p

    "; + + if (is_process_killed($p)) + { + print "

    " . T_("Kill signal sent: Please wait...") . "

    "; + print "

    " . T_("Process is already closed (eg. server was rebooted) - click here to confirm") . "

    "; + } + else + { + print "

    " . T_("Kill the running process") . "

    "; + } + + print process_get_data($p); +} +else +{ + xhtml_head(T_("Monitor system wide case sorting")); + print "

    " . T_("Monitor system wide case sorting") . "

    "; + print "

    " . T_("Click here to enable and begin system wide case sorting") . "

    "; + print "

    " . 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.") . "

    "; + print "

    " . T_("Outcome of last process run (if any)") . "

    "; + print process_get_last_data(2); +} +xhtml_foot(); + +?> diff --git a/admin/systemsortprocess.php b/admin/systemsortprocess.php new file mode 100644 index 00000000..53add884 --- /dev/null +++ b/admin/systemsortprocess.php @@ -0,0 +1,194 @@ + + * @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,"

    " . $buffer . "

    "); + + return ""; //empty buffer +} + +/** + * Disable system sort on shutdown + * + * @author Adam Zammit + * @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(); + +?> diff --git a/config.default.php b/config.default.php index 146d5573..ae54c5c4 100644 --- a/config.default.php +++ b/config.default.php @@ -220,6 +220,11 @@ if (!defined('TAB_INFO')) define('TAB_INFO', true); */ 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 */ diff --git a/database/quexs.sql b/database/quexs.sql index dfa05914..2a9e6c02 100644 --- a/database/quexs.sql +++ b/database/quexs.sql @@ -176,12 +176,14 @@ CREATE TABLE `case` ( `current_operator_id` bigint(20) default NULL, `current_call_id` bigint(20) default NULL, `current_outcome_id` int(11) NOT NULL default '1', + `sortorder` int(11) default NULL, PRIMARY KEY (`case_id`), UNIQUE KEY `onecasepersample` (`sample_id`,`questionnaire_id`), UNIQUE KEY `current_operator_id` (`current_operator_id`), UNIQUE KEY `current_call_id` (`current_call_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; @@ -441,6 +443,7 @@ INSERT INTO `outcome_type` VALUES(5, 'Appointments'); CREATE TABLE `process` ( `process_id` bigint(20) NOT NULL auto_increment, + `type` int(11) NOT NULL default '1', `start` datetime NOT NULL, `stop` datetime default NULL, `kill` tinyint(1) NOT NULL default '0', @@ -522,10 +525,12 @@ CREATE TABLE `questionnaire_sample_exclude_priority` ( `sample_id` bigint(20) NOT NULL, `exclude` tinyint(1) NOT NULL default '0', `priority` tinyint(3) NOT NULL default '50', + `sortorder` int(11) default NULL, PRIMARY KEY (`questionnaire_id`,`sample_id`), KEY `exclude` (`exclude`), 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; -- diff --git a/functions/functions.operator.php b/functions/functions.operator.php index 1d52a0f0..019443af 100644 --- a/functions/functions.operator.php +++ b/functions/functions.operator.php @@ -295,86 +295,122 @@ function get_case_id($operator_id, $create = false) { if ($create) { - /** - * 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 - */ + $systemsort = get_setting('systemsort'); + + if ($systemsort) + { + //Just make sure that this case should go to this operator (assigned to this project and skill) + $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 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) + WHERE c.sortorder IS NOT NULL + AND c.current_operator_id IS NULL + 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 + * + * + * 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"; - $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 + //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 + 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 - * 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) - * - * - * 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) - 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) + } + else + { - 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"; - + /** + * 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) + 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); diff --git a/functions/functions.process.php b/functions/functions.process.php index 6eb2a43b..f74438a1 100644 --- a/functions/functions.process.php +++ b/functions/functions.process.php @@ -43,15 +43,17 @@ include_once(dirname(__FILE__).'/../db.inc.php'); /** * 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 */ -function is_process_running() +function is_process_running($type = 1) { global $db; $sql = "SELECT `process_id` FROM `process` - WHERE `stop` IS NULL"; + WHERE `stop` IS NULL + AND type = '$type'"; $rs = $db->GetRow($sql); @@ -90,23 +92,24 @@ function is_process_killed($process_id) * Start a process * * @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 * * @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 global $db; $db->StartTrans(); - $process = is_process_running(); + $process = is_process_running($type); if ($process == false) { - $sql = "INSERT INTO `process` (`process_id`,`start`,`stop`,`kill`,`data`) - VALUES (NULL,CONVERT_TZ(NOW(),'System','UTC'),NULL,0,'')"; + $sql = "INSERT INTO `process` (`process_id`,`type`,`start`,`stop`,`kill`,`data`) + VALUES (NULL,'$type',CONVERT_TZ(NOW(),'System','UTC'),NULL,0,'')"; $rs = $db->Execute($sql); $args = $db->Insert_ID(); @@ -220,15 +223,17 @@ function process_get_data($process_id) /** * 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 * */ -function process_get_last_data() +function process_get_last_data($type = 1) { global $db; $sql = "SELECT `data` FROM `process` + WHERE type = '$type' ORDER BY `process_id` DESC LIMIT 1";