Skip to content

Commit

Permalink
#104 Optimize code on all data tables pages
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewPoppe committed Aug 14, 2024
1 parent d04260a commit 780f5c8
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 60 deletions.
50 changes: 39 additions & 11 deletions REDCapPRO.php
Original file line number Diff line number Diff line change
Expand Up @@ -1028,29 +1028,57 @@ public function getUserRole(string $username)
public function getAllUsers()
{
$projects = $this->framework->getProjectsWithModuleEnabled();
$staff = $this->getStaff();
$allUsers = $this->getAllUserInfo();
$users = array();
foreach ( $projects as $pid ) {
$project = new Project($this, $pid);
$staff_arr = $project->getStaff();
$all_staff = $staff_arr["allStaff"];
$all_staff = $staff[$pid]["allStaff"];
foreach ( $all_staff as $user ) {
if ( isset($users[$user]) ) {
array_push($users[$user]['projects'], $pid);
} else {
$newUser = $this->framework->getUser($user);
$newUserArr = [
"username" => $user,
"email" => $newUser->getEmail(),
"name" => $this->getUserFullname($user),
"projects" => [ $pid ]
];
$users[$user] = $newUserArr;
$newUser = $allUsers[$user];
$newUser['projects'] = [ $pid ];
$users[$user] = $newUser;
}
}
}
return $users;
}

public function getAllUserInfo() {
$sql = 'SELECT username, user_email AS email, CONCAT(user_firstname, " ", user_lastname) AS name FROM redcap_user_information';
$result = $this->framework->query($sql, []);
$userInfo = [];
while ($row = $result->fetch_assoc()) {
$userInfo[$row['username']] = $row;
}
return $userInfo;
}

public function getStaff()
{
try {
$sql = "SELECT * FROM redcap_external_module_settings
WHERE external_module_id = (SELECT external_module_id FROM redcap_external_modules WHERE directory_prefix = ?)
AND `key` in ('managers', 'users', 'monitors')";
$result = $this->framework->query($sql, [ $this->framework->getModuleInstance()->PREFIX ]);
$staff = [];
while ($row = $result->fetch_assoc()) {
$thisStaff = json_decode($row['value']);
$staff[$row['project_id']][$row['key']] = $thisStaff;
if (!isset($staff[$row['project_id']]['allStaff'])) {
$staff[$row['project_id']]['allStaff'] = [];
}
$staff[$row['project_id']]['allStaff'] = array_merge($staff[$row['project_id']]['allStaff'], $thisStaff);
}

return $staff;
} catch (\Exception $e) {
$this->module->logError("Error getting staff", $e);
}
}

public function safeGetUsername() : string
{
try {
Expand Down
20 changes: 12 additions & 8 deletions src/cc_logs.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

?>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" type="text/css"
href="https://cdn.datatables.net/v/dt/jszip-2.5.0/dt-1.10.25/b-1.7.1/b-colvis-1.7.1/b-html5-1.7.1/cr-1.5.4/date-1.1.0/sb-1.1.0/sp-1.3.0/sl-1.3.3/datatables.min.css" />
<script type="text/javascript"
src="https://cdn.datatables.net/v/dt/jszip-2.5.0/dt-1.10.25/b-1.7.1/b-colvis-1.7.1/b-html5-1.7.1/cr-1.5.4/date-1.1.0/sb-1.1.0/sp-1.3.0/sl-1.3.3/datatables.min.js"
defer></script>
<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.css" rel="stylesheet">

<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.js"></script>

<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" defer></script>
<link rel="stylesheet" type="text/css" href="<?= $module->getUrl("src/css/rcpro_cc.php") ?>">
<?php
Expand Down Expand Up @@ -118,8 +117,12 @@ className: "dt-center"
});
});
},
dom: 'lBfrtip',
stateSave: false,
//dom: 'lBfrtip',
layout: {
topStart: ['pageLength', 'buttons'],
topEnd: 'search'
},
stateSave: true,
stateSaveCallback: function (settings, data) {
localStorage.setItem('DataTables_cclogs_' + settings.sInstance, JSON.stringify(data))
},
Expand Down Expand Up @@ -179,6 +182,7 @@ className: "dt-center"

// Create input element and add event listener
$('<br><input type="text" placeholder="Search ' + title + '" />')
.val(column.search())
.appendTo($(column.header()))
.on('click', function (e) {
e.stopPropagation();
Expand All @@ -191,7 +195,7 @@ className: "dt-center"
});
console.log('End: ', performance.now());

dataTable.columns.adjust().draw();
dataTable.columns.adjust();
},
drawCallback: function (settings) {
t2 = performance.now();
Expand Down
12 changes: 11 additions & 1 deletion src/cc_participants.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
$ui = new UI($module);
$ui->ShowControlCenterHeader("Participants");

?>
<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.css" rel="stylesheet">

<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.js"></script>
<?php
$participantHelper = new ParticipantHelper($module);

if ( $_SERVER["REQUEST_METHOD"] == "POST" ) {
Expand Down Expand Up @@ -283,9 +288,14 @@
}

let dataTable = $('#RCPRO_TABLE').DataTable({
dom: 'lBfrtip',
// dom: 'lBfrtip',
// layout: {
// topStart: ['pageLength'],
// topEnd: 'search'
// },
stateSave: true,
deferRender: true,
processing: true,
ajax: function (data, callback, settings) {
t0 = performance.now();
RCPRO_module.ajax('getParticipantsCC', {})
Expand Down
7 changes: 6 additions & 1 deletion src/cc_projects.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<!DOCTYPE html>
<html lang="en">
<title>REDCapPRO Projects</title>
<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.css" rel="stylesheet">

<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.js"></script>

<link rel="stylesheet" type="text/css" href="<?= $module->getUrl("src/css/rcpro_cc.php") ?>">

<?php
Expand Down Expand Up @@ -49,8 +53,9 @@
const RCPRO_module = <?= $module->getJavascriptModuleObjectName() ?>;
$(document).ready(function () {
let dataTable = $('#RCPRO_TABLE').DataTable({
dom: 'lftip',
// dom: 'lftip',
deferRender: true,
processing: true,
ajax: function (data, callback, settings) {
RCPRO_module.ajax('getProjectsCC', {})
.then(response => {
Expand Down
7 changes: 6 additions & 1 deletion src/cc_staff.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
echo '<link rel="stylesheet" type="text/css" href="' . $module->getUrl("src/css/rcpro_cc.php") . '">';
$module->initializeJavascriptModuleObject();
?>
<link href="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.css" rel="stylesheet">

<script src="https://cdn.datatables.net/v/bs5/jszip-3.10.1/dt-2.1.3/b-3.1.1/b-colvis-3.1.1/b-html5-3.1.1/sr-1.4.1/datatables.min.js"></script>

<div id="loading-container" class="loader-container">
<div id="loading" class="loader"></div>
</div>
Expand Down Expand Up @@ -44,6 +48,7 @@
$(document).ready(function () {
let dataTable = $('#RCPRO_TABLE').DataTable({
deferRender: true,
processing: true,
ajax: function (data, callback, settings) {
RCPRO_module.ajax('getStaffCC', {})
.then(response => {
Expand Down Expand Up @@ -92,7 +97,7 @@ className: "dt-center",
}
},
],
dom: 'lBfrtip',
// dom: 'lBfrtip',
stateSave: true,
stateSaveCallback: function (settings, data) {
localStorage.setItem('DataTables_ccstaff_' + settings.sInstance, JSON.stringify(data))
Expand Down
74 changes: 57 additions & 17 deletions src/classes/AjaxHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function handleAjax()
private function getLogs()
{
try {
if ( $this->params["cc"] && !$this->module->framework->isSuperUser() ) {
if ( $this->params["cc"] === TRUE && !$this->module->framework->isSuperUser() ) {
throw new REDCapProException("You must be an admin to view Control Center logs");
}

Expand Down Expand Up @@ -123,36 +123,54 @@ private function getLogs()
$orderTerm .= ", ";
}
}
// if ( $orderTerm === "" ) {
// $orderTerm = " order by timestamp desc";
// }

//$queryParameters = [ ...$queryParameters, ...$generalSearchParameters ];

// BUILD A FAST QUERY OF THE LOGS

if ($this->params['cc'] === TRUE) {
$columns = REDCapPRO::$logColumnsCC;
$projectClause = "";
} else {
$columns = REDCapPRO::$logColumns;
$projectClause = " AND l.project_id = ?";
}

$sqlFull = "SELECT ";
foreach (REDCapPRO::$logColumnsCC as $key => $column) {
foreach ($columns as $key => $column) {
if ( in_array( $column, $baseColumns, true) ) {
$sqlFull .= " l." . $column;
} else {
$sqlFull .= " MAX(CASE WHEN p.name = '" . $column . "' THEN p.value END) AS $column ";
}
if ($key !== array_key_last(REDCapPRO::$logColumnsCC)) {
if ($key !== array_key_last($columns)) {
$sqlFull .= ", ";
}
}

// $newSql = "SELECT l.log_id, l.timestamp, l.ui_id, l.ip, l.project_id, l.record, l.message, group_concat(p.name SEPARATOR '||') name, group_concat(p.value SEPARATOR '||') value
// FROM redcap_external_modules_log l
// LEFT JOIN redcap_external_modules_log_parameters p
// ON p.log_id = l.log_id
// LEFT JOIN redcap_external_modules_log_parameters module_token
// ON module_token.log_id = l.log_id
// AND module_token.name = 'module_token'
// WHERE l.external_module_id = (SELECT external_module_id FROM redcap_external_modules WHERE directory_prefix = ?)
// AND module_token.value = ?
// AND p.name IN ('".implode("','", $columns)."')
// GROUP BY l.log_id";



$sql = " from redcap_external_modules_log l
left join redcap_external_modules_log_parameters p
ON p.log_id = l.log_id
left join redcap_external_modules_log_parameters module_token
on module_token.log_id = l.log_id
and module_token.name = 'module_token'
WHERE l.external_module_id = (SELECT external_module_id FROM redcap_external_modules WHERE directory_prefix = ?)
and module_token.value = ? ";
and module_token.value = ? " . $projectClause;
array_push($fastQueryParameters, $this->module->framework->getModuleInstance()->PREFIX, $this->module->getModuleToken());
if (!$this->params['cc']) {
array_push($fastQueryParameters, $this->project_id);
}

if ( $generalSearchTerm !== "" ) {
$sql .= " and (";
Expand All @@ -176,7 +194,7 @@ private function getLogs()
$queryResult = $this->module->framework->query($sqlFull . $sql . $orderTerm . $limitTerm, $fastQueryParameters);
$countResult = $this->module->framework->query("SELECT * " . $sql, $fastQueryParameters)->num_rows;

// $queryText = "SELECT " . implode(', ', REDCapPRO::$logColumnsCC) . " WHERE " . $generalSearchText . $orderTerm . $limitTerm;
// $queryText = "SELECT " . implode(', ', $columns) . " WHERE " . $generalSearchText . $orderTerm . $limitTerm;
// $queryResult = $this->module->queryLogs($queryText, $queryParameters);
// $countResult = $this->module->countLogs($generalSearchText, $queryParameters);
$totalCountResult = $this->module->countLogs("module_token = ?", [ $this->module->getModuleToken() ]);
Expand Down Expand Up @@ -271,6 +289,7 @@ private function getParticipants()
// TIMING
$lastTime = microtime(true);
$times = ["Start" => $lastTime];
$startTime = $lastTime;

$role = $this->module->getUserRole($this->module->safeGetUsername());
if ( $role < 1 ) {
Expand All @@ -285,17 +304,39 @@ private function getParticipants()
try {
$participantHelper = new ParticipantHelper($this->module);
$participantList = $participantHelper->getProjectParticipants($rcpro_project_id, $rcpro_user_dag);
// TIMING
$now = microtime(true);
$times["Get Participants"] = $now - $lastTime;
$lastTime = $now;

$links = $projectHelper->getAllLinks();
// TIMING
$now = microtime(true);
$times["Before Loop"] = $now - $lastTime;
$times["Get Links"] = $now - $lastTime;
$lastTime = $now;

foreach ( $participantList as $participant ) {
$rcpro_participant_id = (int) $participant["log_id"];
$link_id = $projectHelper->getLinkId($rcpro_participant_id, $rcpro_project_id);
$dag_id = $dagHelper->getParticipantDag($link_id);
//$link_id = $projectHelper->getLinkId($rcpro_participant_id, $rcpro_project_id);
// $link_id = $links[$rcpro_participant_id][$rcpro_project_id]['link_id'];
// // TIMING
// $now = microtime(true);
// $times["Get Link ID"] = $now - $lastTime;
// $lastTime = $now;

// $dag_id = $dagHelper->getParticipantDag($link_id);
$dag_id = $links[$rcpro_participant_id][$rcpro_project_id]['dag_id'];
// TIMING
$now = microtime(true);
$times["Get DAG ID"] = $now - $lastTime;
$lastTime = $now;

$dag_name = $dagHelper->getDagName($dag_id) ?? "Unassigned";
// TIMING
$now = microtime(true);
$times["Get DAG Name"] = $now - $lastTime;
$lastTime = $now;

$thisParticipant = [
'username' => $participant["rcpro_username"] ?? "",
'password_set' => $participant["pw_set"] === 'True',
Expand All @@ -313,9 +354,8 @@ private function getParticipants()

// TIMING
$now = microtime(true);
$times["After Loop"] = $now - $lastTime;
$lastTime = $now;
$this->module->log('times', [ 'times' => json_encode($times) ]);
$times["End"] = $now - $startTime;
$this->module->log('times', [ 'times' => json_encode($times, JSON_PRETTY_PRINT) ]);
} catch ( \Throwable $e ) {
$this->module->logError('Error fetching participant list', $e);
} finally {
Expand Down
44 changes: 43 additions & 1 deletion src/classes/ParticipantHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ public function getAllParticipantProjects()
*
* @return array|NULL participants enrolled in given study
*/
public function getProjectParticipants(string $rcpro_project_id, ?int $dag = NULL)
public function getProjectParticipantsOld(string $rcpro_project_id, ?int $dag = NULL)
{
$SQL = "SELECT rcpro_participant_id WHERE message = 'LINK' AND rcpro_project_id = ? AND active = 1 AND (project_id IS NULL OR project_id IS NOT NULL)";
$PARAMS = [ $rcpro_project_id ];
Expand All @@ -564,6 +564,48 @@ public function getProjectParticipants(string $rcpro_project_id, ?int $dag = NUL
}
}

/**
* get array of active enrolled participants given a rcpro project id
*
* @param string $rcpro_project_id Project ID (not REDCap PID!)
* @param int|NULL $dag Data Access Group to filter search by
*
* @return array|NULL participants enrolled in given study
*/
public function getProjectParticipants(string $rcpro_project_id, ?int $dag = NULL)
{
$SQL1 = "SELECT rcpro_participant_id WHERE message = 'LINK' AND rcpro_project_id = ? AND active = 1 AND (project_id IS NULL OR project_id IS NOT NULL)";
$PARAMS1 = [ $rcpro_project_id ];
if ( isset($dag) ) {
$SQL1 .= " AND project_dag IS NOT NULL AND project_dag = ?";
array_push($PARAMS1, strval($dag));
}

$SQL2 = "SELECT log_id, rcpro_username, email, fname, lname, lockout_ts, pw WHERE message = 'PARTICIPANT' AND (project_id IS NULL OR project_id IS NOT NULL)";

try {
$result1 = $this->module->selectLogs($SQL1, $PARAMS1);
$result2 = $this->module->selectLogs($SQL2, []);

$allParticipants = array();
while ( $row = $result2->fetch_assoc() ) {
$allParticipants[$row['log_id']] = $row;
}

$participants = array();
while ( $row = $result1->fetch_assoc() ) {
$participant = $allParticipants[$row['rcpro_participant_id']];
$participant["pw_set"] = (!isset($participant["pw"]) || $participant["pw"] === "") ? "False" : "True";
unset($participant["pw"]);
$participants[$row['rcpro_participant_id']] = $participant;
}

return $participants;
} catch ( \Exception $e ) {
$this->module->logError("Error fetching project participants", $e);
}
}

/**
* Fetch username for given participant id
*
Expand Down
Loading

0 comments on commit 780f5c8

Please sign in to comment.