diff --git a/REDCapPRO.php b/REDCapPRO.php
index 71086a2..ae08137 100644
--- a/REDCapPRO.php
+++ b/REDCapPRO.php
@@ -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 {
diff --git a/src/cc_logs.php b/src/cc_logs.php
index 19804e6..1401b06 100644
--- a/src/cc_logs.php
+++ b/src/cc_logs.php
@@ -16,11 +16,10 @@
?>
-
-
+
+
+
+
">
')
+ .val(column.search())
.appendTo($(column.header()))
.on('click', function (e) {
e.stopPropagation();
@@ -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();
diff --git a/src/cc_participants.php b/src/cc_participants.php
index fb33d49..4e63285 100644
--- a/src/cc_participants.php
+++ b/src/cc_participants.php
@@ -28,6 +28,11 @@
$ui = new UI($module);
$ui->ShowControlCenterHeader("Participants");
+?>
+
+
+
+
REDCapPRO Projects
+
+
+
+
">
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 => {
diff --git a/src/cc_staff.php b/src/cc_staff.php
index e485318..7974938 100644
--- a/src/cc_staff.php
+++ b/src/cc_staff.php
@@ -16,6 +16,10 @@
echo '';
$module->initializeJavascriptModuleObject();
?>
+
+
+
+
@@ -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 => {
@@ -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))
diff --git a/src/classes/AjaxHandler.php b/src/classes/AjaxHandler.php
index ce7983e..a1760b6 100644
--- a/src/classes/AjaxHandler.php
+++ b/src/classes/AjaxHandler.php
@@ -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");
}
@@ -123,27 +123,42 @@ 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
@@ -151,8 +166,11 @@ private function getLogs()
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 (";
@@ -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() ]);
@@ -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 ) {
@@ -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',
@@ -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 {
diff --git a/src/classes/ParticipantHelper.php b/src/classes/ParticipantHelper.php
index 059961a..e023b0e 100644
--- a/src/classes/ParticipantHelper.php
+++ b/src/classes/ParticipantHelper.php
@@ -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 ];
@@ -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
*
diff --git a/src/classes/ProjectHelper.php b/src/classes/ProjectHelper.php
index b98b344..7b3b21a 100644
--- a/src/classes/ProjectHelper.php
+++ b/src/classes/ProjectHelper.php
@@ -176,6 +176,23 @@ public function getLinkId(int $rcpro_participant_id, int $rcpro_project_id)
}
}
+ public function getAllLinks() {
+ $SQL = "SELECT log_id, rcpro_participant_id, rcpro_project_id, project_dag WHERE message = 'LINK' AND (project_id IS NULL OR project_id IS NOT NULL)";
+ try {
+ $result = $this->module->selectLogs($SQL, []);
+ $links = [];
+ while ($row = $result->fetch_assoc()) {
+ $links[$row['rcpro_participant_id']][$row['rcpro_project_id']] = [
+ 'log_id' => $row['log_id'],
+ 'dag_id' => $row['project_dag']
+ ];
+ }
+ return $links;
+ } catch ( \Exception $e ) {
+ $this->module->logError("Error fetching links", $e);
+ }
+ }
+
/**
* Get the REDCap PID corresponding with a project ID
*
diff --git a/src/logs.php b/src/logs.php
index ec83616..4dd12d1 100644
--- a/src/logs.php
+++ b/src/logs.php
@@ -19,11 +19,9 @@
= $module->APPTITLE ?> - Enroll
-
-
+
+
+
" />
@@ -63,10 +61,23 @@ function logExport(type) {
$(document).ready(function () {
$('#RCPRO_TABLE').DataTable({
deferRender: true,
+ serverSide: true,
+ processing: true,
+ searchDelay: 1000,
+ order: [[0, 'desc']],
ajax: function (data, callback, settings) {
- RCPRO_module.ajax('getLogs', { cc: false })
+ const payload = {
+ draw: data.draw,
+ search: data.search,
+ start: data.start,
+ length: data.length,
+ order: data.order,
+ columns: data.columns,
+ cc: false
+ }
+ RCPRO_module.ajax('getLogs', payload)
.then(response => {
- callback({ data: response });
+ callback(response);
})
.catch(error => {
console.error(error);
@@ -97,7 +108,11 @@ function logExport(type) {
});
});
},
- dom: 'lBfrtip',
+ // dom: 'lBfrtip',
+ layout: {
+ topStart: ['pageLength', 'buttons'],
+ topEnd: 'search'
+ },
stateSave: true,
stateSaveCallback: function (settings, data) {
localStorage.setItem('DataTables_logs_' + settings.sInstance, JSON.stringify(data))
@@ -105,17 +120,18 @@ function logExport(type) {
stateLoadCallback: function (settings) {
return JSON.parse(localStorage.getItem('DataTables_logs_' + settings.sInstance))
},
- colReorder: true,
- buttons: [{
- extend: 'searchPanes',
- config: {
- cascadePanes: true,
- }
+ colReorder: false,
+ buttons: [
+ // {
+ // extend: 'searchPanes',
+ // config: {
+ // cascadePanes: true,
+ // }
- },
- {
- extend: 'searchBuilder',
- },
+ // },
+ // {
+ // extend: 'searchBuilder',
+ // },
'colvis',
{
text: 'Restore Default',
@@ -148,7 +164,10 @@ function logExport(type) {
scrollX: true,
scrollY: '50vh',
scrollCollapse: true,
- pageLength: 100
+ // pageLength: 100,
+ initComplete: function() {
+ $('#RCPRO_TABLE').DataTable().columns.adjust();
+ },
});
$('#logs').removeClass('dataTableParentHidden');
@@ -161,7 +180,7 @@ function logExport(type) {
$('.dt-button-collection').draggable();
}
});
- $('#RCPRO_TABLE').DataTable().columns.adjust().draw();
+ //$('#RCPRO_TABLE').DataTable().columns.adjust().draw();
});
}(window.jQuery, window, document));
diff --git a/src/manage-users.php b/src/manage-users.php
index 53f45d3..9989b45 100644
--- a/src/manage-users.php
+++ b/src/manage-users.php
@@ -164,6 +164,7 @@ function handleButtons() {
const dt = $('#RCPRO_TABLE').DataTable({
deferRender: true,
+ processing: true,
ajax: function (data, callback, settings) {
RCPRO_module.ajax('getStaff', {})
.then(response => {
diff --git a/src/manage.php b/src/manage.php
index 3d2e77a..7f105f7 100644
--- a/src/manage.php
+++ b/src/manage.php
@@ -16,6 +16,7 @@
$ui->ShowHeader("Manage");
echo "" . $module->APPTITLE . " - Manage";
+
// Check for errors
if ( isset($_GET["error"]) ) {
?>
@@ -565,6 +566,7 @@ className: 'dt-center'
let datatable = $('#RCPRO_TABLE').DataTable({
deferRender: true,
+ processing: true,
ajax: function (data, callback, settings) {
RCPRO_module.ajax('getParticipants', {})
.then(response => {