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:
@@ -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 "<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=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)
|
||||
{
|
||||
|
||||
100
admin/systemsort.php
Normal file
100
admin/systemsort.php
Normal 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
194
admin/systemsortprocess.php
Normal 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();
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user