Skip to content

[dashboard] New Study Progression Panel #9789

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
46 changes: 46 additions & 0 deletions modules/behavioural_qc/php/module.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,50 @@ class Module extends \Module
{
return dgettext("behavioural_qc", "Behavioural Quality Control");
}

/**
* {@inheritDoc}
*
* @param string $type The type of widgets to get.
* @param \User $user The user widgets are being retrieved for.
* @param array $options A type dependent list of options to provide
* to the widget.
*
* @return \LORIS\GUI\Widget[]
*/
public function getWidgets(string $type, \User $user, array $options) : array
{
$factory = \NDB_Factory::singleton();
$baseURL = $factory->settings()->getBaseURL();
switch ($type) {
case 'study-progression':
$DB = $factory->database();
$data = $DB->pselectWithIndexKey(
"SELECT
p.ProjectID,
COUNT(*) AS count,
'rgb(252, 241, 255)' as colour,
CONCAT('$baseURL/behavioural_qc/?Project=', p.ProjectID)
AS url
FROM flag f
JOIN session s ON f.SessionID = s.ID
JOIN Project p ON p.ProjectID = s.ProjectID
WHERE DataID IS NOT NULL
AND s.Active <> 'N'
AND s.CenterID <> 1
AND f.CommentID NOT LIKE 'DDE_%'
GROUP BY p.Name",
[],
'ProjectID'
);
return [
new \LORIS\dashboard\DataWidget(
"Behavioural Session",
$data,
"",
)
];
}
return [];
}
}
53 changes: 53 additions & 0 deletions modules/candidate_list/php/module.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,57 @@ class Module extends \Module
return true;
}

/**
* {@inheritDoc}
*
* @param string $type The type of widgets to get.
* @param \User $user The user widgets are being retrieved for.
* @param array $options A type dependent list of options to provide
* to the widget.
*
* @return \LORIS\GUI\Widget[]
*/
public function getWidgets(string $type, \User $user, array $options) : array
{
$factory = \NDB_Factory::singleton();
$baseURL = $factory->settings()->getBaseURL();
switch ($type) {
case 'study-progression':
$DB = $factory->database();
$data = $DB->pselectWithIndexKey(
"SELECT
ProjectID,
COUNT(DISTINCT PSCID) AS count,
'rgb(255, 252, 199)' AS colour,
CONCAT('$baseURL/candidate_list/?project=', ProjectName)
AS url,
ProjectName
FROM (
SELECT
c.PSCID,
COALESCE(p.ProjectID, p2.ProjectID) AS ProjectID,
COALESCE(p.Name, p2.Name) AS ProjectName
FROM candidate c
LEFT JOIN session s ON s.CandidateID = c.ID
LEFT JOIN Project p ON p.ProjectID = s.ProjectID
JOIN Project p2 ON c.RegistrationProjectID = p2.ProjectID
WHERE c.Active <> 'N'
AND s.Active <> 'N'
AND s.CenterID <> 1
) AS sub
GROUP BY ProjectID, ProjectName;",
[],
'ProjectID'
);
return [
new \LORIS\dashboard\DataWidget(
"Participant",
$data,
"",
)
];
}
return [];
}

}
90 changes: 90 additions & 0 deletions modules/dashboard/php/datawidget.class.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php declare(strict_types=1);

/**
* File contains the \LORIS\dashboard\DataWidget class.
*
* PHP Version 8
*
* @category Main
* @package Loris
* @author Saagar Arya <[email protected]>
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
* @link https://www.github.com/aces/Loris
*/
namespace LORIS\dashboard;

/**
* A DataWidget is a type of dashboard widget which contains data from
* a database query, a label, and a link to go to when the user clicks on it.
*
* @category Main
* @package Loris
* @author Saagar Arya <[email protected]>
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
* @link https://www.github.com/aces/Loris
*/
class DataWidget implements \LORIS\GUI\Widget
{
protected $label;
protected $data;
protected $cssclass;

/**
* Construct a DataWidget with the given properties.
*
* @param string $label The label to describe the data.
* @param array $data The array data.
* @param string $cssclass A CSS class to add to the element.
*/
public function __construct(
string $label,
array $data,
string $cssclass,
) {
$this->label = $label;
$this->data = $data;
$this->cssclass = $cssclass;
}

/**
* Returns the label for this task.
*
* @return string
*/
public function label() : string
{
return $this->label;
}

/**
* Returns the number associated with this task.
*
* @return array
*/
public function data() : array
{
return $this->data;
}

/**
* If non-empty, add this class name to the task item.
*
* @return string
*/
public function CSSClass() : string
{
return $this->cssclass;
}

/**
* TaskWidgets get serialized to a string by the MyTasks panel.
*
* @return string
*/
public function __toString()
{
// The dashboard module just uses the methods on this
// to get metadata, it handles the rendering itself.
return "";
}
}
8 changes: 3 additions & 5 deletions modules/dashboard/test/DashboardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -704,10 +704,8 @@ private function _testPlan5And6()
);
}
/**
* 7. Check that scans per site (study progression panel) view is correct
* (scan dates and scan numbers).
* 8. Check that recruitment per site view is correct
* (study progression panel).
* 7. Check that study progression panel is correct.
* 8. Check that there is no error message in the panel.
*
* @return void
*/
Expand All @@ -721,7 +719,7 @@ private function _testPlan7And8()
)->getText();

$this->assertStringContainsString(
"Scan sessions per site",
"Participants",
$testText
);

Expand Down
25 changes: 25 additions & 0 deletions modules/imaging_browser/php/module.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,31 @@ class Module extends \Module
1,
)
];
case 'study-progression':
$DB = $factory->database();
$data = $DB->pselectWithIndexKey(
"SELECT
p.ProjectID,
p.Name AS ProjectName,
COUNT(s.ID) AS count,
'rgb(235, 255, 254)' as colour,
'".$baseURL."/imaging_browser' as url
FROM session s
JOIN Project p ON p.ProjectID = s.ProjectID
JOIN mri_upload mu ON mu.SessionID = s.ID
WHERE s.Active <> 'N'
AND s.CenterID <> 1
GROUP BY p.Name",
[],
'ProjectID'
);
return [
new \LORIS\dashboard\DataWidget(
"Imaging Session",
$data,
"",
)
];
}
return [];
}
Expand Down
27 changes: 26 additions & 1 deletion modules/statistics/css/recruitment.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@
display: none !important;
}

.study-progression-container {
flex-direction: row;
display: flex;
border-bottom: 1px solid #ccc;
margin-bottom: 10px;
}

.study-progression-button {
background-color: #ffffff;
border-radius: 15px;
padding: 10px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.25);
transition: all 0.3s ease;
text-align: center;
margin: 0 10px 10px 0;
padding: 10px;
}

.study-progression-button:hover {
transform: translateY(-10px);
background-color: #f9fdff;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
border: 1px solid #acacac;
}

/* ===== Chart Header (Title + Dropdown) ===== */
.chart-header {
display: flex;
Expand Down Expand Up @@ -104,7 +129,7 @@
background-color: #f9fdff;
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
border: 1px solid #acacac;
}
}

/* Ensure the form spans full width */
.site-breakdown-filters {
Expand Down
3 changes: 1 addition & 2 deletions modules/statistics/jsx/widgets/recruitment.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {setupCharts} from './helpers/chartBuilder';

/**
* Recruitment - a widget containing statistics for recruitment data.
*
* @param {object} props
* @return {JSX.Element}
*/
Expand Down Expand Up @@ -163,7 +162,7 @@ const Recruitment = (props) => {
>
{Object.entries(json['recruitment']).map(
([key, value]) => {
if (key !== 'overall') {
if (key !== 'overall' && value['total_recruitment'] > 0) {
return <div key ={`projectBreakdown_${key}`}>
{progressBarBuilder(value)}
</div>;
Expand Down
63 changes: 60 additions & 3 deletions modules/statistics/jsx/widgets/studyprogression.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {setupCharts} from './helpers/chartBuilder';

/**
* StudyProgression - a widget containing statistics for study data.
*
* @param {object} props
* @return {JSX.Element}
*/
Expand Down Expand Up @@ -86,6 +85,64 @@ const StudyProgression = (props) => {
}
}}
views={[
{
content:
// ############ CBIGR OVERRIDE START ############
<div
style={{
maxHeight: '400px',
overflowY: 'scroll',
overflowX: 'hidden',
}}
>
{Object.entries(json['studyprogression']
['progressionData']).map(
([projectName, projectData]) => {
if (projectData.length > 0) {
return <div key={`progress_${projectName}`}>
<h3 style={{marginTop: 0}}>{projectName}</h3>
<div
className='study-progression-container'
>
{projectData.map((data) => {
const commonProps = {
className: 'study-progression-button',
style: {
backgroundColor: data['colour'],
color: 'black',
textDecoration: 'none',
},
key: `progress_${projectName}_${data['title']}`,
};

const content = (
<>
<h4>{data['count']}</h4>
<div>
{data['title'].replace('_', ' ')}
{data['count'] !== 1 && 's'}
</div>
</>
);

return data['url'] ? (
<a {...commonProps} href={data['url']}>
{content}
</a>
) : (
<div {...commonProps}>
{content}
</div>
);
})}
</div>
</div>;
}
}
)}
</div>,
title: 'Study Progression - summary',
},
{
content: json['studyprogression']['total_scans'] > 0 ? (
<div
Expand Down Expand Up @@ -170,8 +227,8 @@ const StudyProgression = (props) => {
StudyProgression.propTypes = {
data: PropTypes.object,
baseURL: PropTypes.string,
updateFilters: PropTypes.function,
showChart: PropTypes.function,
updateFilters: PropTypes.func,
showChart: PropTypes.func,
};
StudyProgression.defaultProps = {
data: {},
Expand Down
Loading
Loading