Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[candidate_list] Make candidate_list use LORIS streamable binary format #9195

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ mri_violations:
issue_tracker:
target=issue_tracker npm run compile

candidate_list:
target=candidate_list npm run compile

candidate_parameters:
target=candidate_parameters npm run compile

Expand Down
54 changes: 29 additions & 25 deletions modules/candidate_list/jsx/candidateListIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Loader from 'Loader';
import FilterableDataTable from 'FilterableDataTable';
import Modal from 'Modal';

import fetchDataStream from 'jslib/fetchDataStream';
import OpenProfileForm from './openProfileForm';

/**
Expand All @@ -25,10 +26,11 @@ class CandidateListIndex extends Component {
super(props);

this.state = {
data: {},
data: [],
error: false,
isLoaded: false,
hideFilter: true,
fieldOptions: {},
show: {profileForm: false},
};

Expand Down Expand Up @@ -63,8 +65,18 @@ class CandidateListIndex extends Component {
* Called by React when the component has been rendered on the page.
*/
componentDidMount() {
this.fetchData()
.then(() => this.setState({isLoaded: true}));
fetch('options',
{credentials: 'same-origin'}).then(
(resp) => resp.json()
).then(
(json) => {
this.setState({
fieldOptions: json,
});
}
);

this.fetchData();

const searchParams = new URLSearchParams(location.search);
if (searchParams.has('hide')) {
Expand All @@ -80,23 +92,15 @@ class CandidateListIndex extends Component {
* @return {object}
*/
fetchData() {
return fetch(this.props.dataURL, {credentials: 'same-origin'})
.then((resp) => resp.json())
.then((data) => {
// Convert concatenated string of cohort and visit labels to array
data.Data = data.Data.map((row) => {
// Visit label
row[2] = (row[2]) ? row[2].split(',') : null;
// Cohort
row[4] = (row[4]) ? row[4].split(',') : null;
return row;
});
this.setState({data});
})
.catch((error) => {
this.setState({error: true});
console.error(error);
});
fetchDataStream(this.props.dataURL,
(row) => this.state.data.push(row),
(end) => {
this.setState({data: this.state.data});
},
() => {
this.setState({isLoaded: true});
},
);
}

/**
Expand Down Expand Up @@ -149,8 +153,7 @@ class CandidateListIndex extends Component {
}

if (column === 'Cohort') {
// If user has multiple cohorts, join array into string
let result = (cell) ? <td>{cell.join(', ')}</td> : <td></td>;
let result = (cell) ? <td>{cell}</td> : <td></td>;
return result;
}

Expand Down Expand Up @@ -178,7 +181,8 @@ class CandidateListIndex extends Component {
* XXX: Currently, the order of these fields MUST match the order of the
* queried columns in _setupVariables() in candidate_list.class.inc
*/
const options = this.state.data.fieldOptions;
// const options = this.state.data.fieldOptions;
const options = this.state.fieldOptions;
const fields = [
{
label: 'PSCID',
Expand Down Expand Up @@ -371,7 +375,7 @@ class CandidateListIndex extends Component {
{profileForm}
<FilterableDataTable
name="candidateList"
data={this.state.data.Data}
data={this.state.data}
fields={fields}
actions={actions}
getFormattedCell={this.formatColumn}
Expand All @@ -394,7 +398,7 @@ window.addEventListener('load', () => {
document.getElementById('lorisworkspace')
).render(
<CandidateListIndex
dataURL={`${loris.BaseURL}/candidate_list/?format=json`}
dataURL={`${loris.BaseURL}/candidate_list/?format=binary`}
hasPermission={loris.userHasPermission}
baseURL={loris.BaseURL}
betaProfileLink={args['betaprofile']}
Expand Down
109 changes: 109 additions & 0 deletions modules/candidate_list/php/options.class.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php declare(strict_types=1);
namespace LORIS\candidate_list;
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;

/**
* Implements the candidate_list menu
*
* @category Main
* @package Candidate_List
* @author Loris Team <loris.mni@bic.mni.mcgill.ca>
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
* @link https://github.com/aces/Loris/
*/
class Options extends \LORIS\Http\Endpoint
{
/**
* Overloading this method to allow access to site users (their own site only)
* and users w/ multisite privs
*
* @param \User $user The user whose access is being checked
*
* @return bool true if user has access, false otherwise
*/
function _hasAccess(\User $user) : bool
{
return (
$user->hasPermission('access_all_profiles')
|| ($user->hasStudySite() && $user->hasPermission('data_entry'))

);
}

/**
* Return the list of field options required to be serialized to JSON
* in order to render the frontend.
*
* @return array
*/
function getFieldOptions() : array
{
$this->loris->getModule('candidate_parameters')->registerAutoloader();

// create user object
$factory = \NDB_Factory::singleton();
$user = $factory->user();
$config = $factory->config();

// get the list of visit labels
$visit_label_options = \Utility::getVisitList();

// get the list of sites available for the user
if ($user->hasPermission('access_all_profiles')) {
$list_of_sites = \Utility::getSiteList();
} else {
$list_of_sites = $user->getStudySites();
}
$site_options = [];
foreach (array_values($list_of_sites) as $name) {
$site_options[$name] = $name;
}

// get the list of projects
$list_of_projects = \Utility::getProjectList();
$project_options = [];
foreach (array_values($list_of_projects) as $name) {
$project_options[$name] = $name;
}

// get the list of cohorts
$list_of_cohorts = \Utility::getCohortList();
$cohort_options = [];
foreach (array_values($list_of_cohorts) as $name) {
$cohort_options[$name] = $name;
}

// get the list participant status options
$list_of_participant_status
= \Candidate::getParticipantStatusOptions();
$participant_status_options = [];
foreach (array_values($list_of_participant_status) as $name) {
$participant_status_options[$name] = $name;
}

return [
'visitlabel' => $visit_label_options,
'site' => $site_options,
'project' => $project_options,
'cohort' => $cohort_options,
'participantstatus' => $participant_status_options,
'useedc' => $config->getSetting("useEDC"),
'Sex' => \Utility::getSexList(),
];
}

/**
* An Endpoint acts as middleware which will calculate ETag if applicable.
*
* @param ServerRequestInterface $request The incoming PSR7 request
*
* @return ResponseInterface The outgoing PSR7 response
*/
public function handle(
ServerRequestInterface $request,
): ResponseInterface {
return new \LORIS\Http\Response\JSON\OK($this->getFieldOptions());
}
}

Loading