Skip to content

Commit

Permalink
[Login] Summary Statistics (#9518)
Browse files Browse the repository at this point in the history
- Added a customizable summary of LORIS statistics to the Login page for
guest users to see
- A new table was created that is populated with the counts of pinned
dataquery queries, and separated by project
- This table is read by the login module for projects with
`showSummaryOnLogin` as true in the Project database table.
- If no data is populated, then the summary is not shown on the login
page
- Data is populated by the new tool:
`tools/update_login_summary_statistics.php`. It must be run for the data
in the summary to be updated. Can be added to a project's nightly cron,
or at each release
- Some queries are not necessarily possible in the DQT, so in-order to
keep it customizable for projects, I added the ability to add SQL files
to a folder in project/tools/Login_Summary_Statistics that can be added
to the summary list.
- There is a clipboard icon at the bottom right where if you press it,
your clipboard will be filled with a csv version of the data
  • Loading branch information
skarya22 authored Feb 12, 2025
1 parent af6b5e0 commit 1e706f2
Show file tree
Hide file tree
Showing 25 changed files with 775 additions and 43 deletions.
11 changes: 10 additions & 1 deletion SQL/0000-00-00-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CREATE TABLE `Project` (
`Name` VARCHAR(255) NOT NULL,
`Alias` char(4) NOT NULL,
`recruitmentTarget` INT(6) Default NULL,
`showSummaryOnLogin` BOOLEAN DEFAULT TRUE,
PRIMARY KEY (`ProjectID`),
UNIQUE KEY `u_ProjectName` (`Name`)
) ENGINE = InnoDB DEFAULT CHARSET=utf8;
Expand Down Expand Up @@ -2502,6 +2503,14 @@ CREATE TABLE `publication_users_edit_perm_rel` (
CONSTRAINT `FK_publication_users_edit_perm_rel_UserID` FOREIGN KEY (`UserID`) REFERENCES `users` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET='utf8';

CREATE TABLE Login_Summary_Statistics (
Title VARCHAR(255),
Project VARCHAR(255),
Value INT,
QueryOrder INT,
PRIMARY KEY (Title, Project)
);

CREATE TABLE dataquery_queries (
QueryID int(10) unsigned NOT NULL AUTO_INCREMENT,
Query JSON NOT NULL,
Expand Down Expand Up @@ -2561,7 +2570,7 @@ CREATE TABLE dataquery_study_queries_rel (
-- to a saved query but is chosen by admins, a dashboard query
-- shows the number of matching results on the LORIS dashboard.
Name varchar(255) NOT NULL,
PinType enum('topquery', 'dashboard'),
PinType enum('topquery', 'dashboard', 'loginpage'),
FOREIGN KEY (QueryID) REFERENCES dataquery_queries(QueryID),
FOREIGN KEY (PinnedBy) REFERENCES users(ID),
CONSTRAINT unique_pin UNIQUE (QueryID, PinType)
Expand Down
1 change: 1 addition & 0 deletions SQL/9999-99-99-drop_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ DROP TABLE IF EXISTS dataquery_shared_queries_rel;
DROP TABLE IF EXISTS dataquery_run_queries;
DROP TABLE IF EXISTS dataquery_query_names;
DROP TABLE IF EXISTS dataquery_queries;
DROP TABLE IF EXISTS `Login_Summary_Statistics`;

-- 0000-00-05-ElectrophysiologyTables.sql
DROP TABLE IF EXISTS `physiological_event_parameter_category_level`;
Expand Down
9 changes: 9 additions & 0 deletions SQL/Login_Summary_Statistics/01_Female Participant.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(DISTINCT c.CandID) AS count
FROM candidate c
JOIN Project ON c.RegistrationProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
AND c.Sex = 'Female'
AND Entity_type = 'Human'
GROUP BY Project.Name WITH ROLLUP
9 changes: 9 additions & 0 deletions SQL/Login_Summary_Statistics/02_Male Participant.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(DISTINCT c.CandID) AS count
FROM candidate c
JOIN Project ON c.RegistrationProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
AND c.Sex = 'Male'
AND Entity_type = 'Human'
GROUP BY Project.Name WITH ROLLUP
8 changes: 8 additions & 0 deletions SQL/Login_Summary_Statistics/03_Site.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(DISTINCT psc.CenterID) AS count
FROM psc
JOIN session s ON s.CenterID = psc.CenterID
JOIN Project ON s.ProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
GROUP BY Project.Name WITH ROLLUP
7 changes: 7 additions & 0 deletions SQL/Login_Summary_Statistics/04_Visit.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(CandID) AS count
FROM session s
JOIN Project ON s.ProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
GROUP BY Project.Name WITH ROLLUP
8 changes: 8 additions & 0 deletions SQL/Login_Summary_Statistics/05_Instrument.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(DISTINCT TestID) AS count
FROM flag f
JOIN session s ON s.ID = f.SessionID
JOIN Project ON s.ProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
GROUP BY Project.Name WITH ROLLUP;
8 changes: 8 additions & 0 deletions SQL/Login_Summary_Statistics/06_Scan.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(FileID) AS count
FROM files f
JOIN session s ON s.ID = f.SessionID
JOIN Project ON s.ProjectID = Project.ProjectID
WHERE Project.showSummaryOnLogin = 1
GROUP BY Project.Name WITH ROLLUP;
17 changes: 17 additions & 0 deletions SQL/Login_Summary_Statistics/07_EEG Recording.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SELECT
IFNULL(Project.Name, 'All Projects') as ProjectName,
COUNT(PhysiologicalFileID) AS count
FROM physiological_parameter_file ppf
LEFT JOIN physiological_file USING (PhysiologicalFileID)
LEFT JOIN physiological_output_type USING (PhysiologicalOutputTypeID)
LEFT JOIN Project ON ppf.ProjectID = Project.ProjectID
WHERE (
ParameterTypeID = (
SELECT ParameterTypeID
FROM parameter_type
WHERE Name = 'RecordingDuration'
)
)
-- AND OutputTypeName = 'raw'
AND Project.showSummaryOnLogin = 1
GROUP BY Project.Name WITH ROLLUP
13 changes: 13 additions & 0 deletions SQL/New_patches/2024-12-18-Login_statistics.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE TABLE Login_Summary_Statistics (
Title VARCHAR(255),
Project VARCHAR(255),
Value INT,
QueryOrder INT,
PRIMARY KEY (Title, Project)
);

ALTER TABLE dataquery_study_queries_rel
MODIFY COLUMN PinType enum('topquery','dashboard', 'loginpage') DEFAULT NULL;

ALTER TABLE Project
ADD COLUMN showSummaryOnLogin BOOLEAN DEFAULT TRUE;
28 changes: 21 additions & 7 deletions modules/dataquery/jsx/welcome.adminquerymodal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ function AdminQueryModal(props: {
QueryID: number,
defaultName: string,
closeModal: () => void,
onSubmit: (name: string, topQuery: boolean, dashboardQuery: boolean)
onSubmit: (
name: string,
topQuery: boolean,
dashboardQuery: boolean,
loginQuery: boolean,
)
=> void,
}) {
const [queryName, setQueryName] = useState(props.defaultName || '');
const [topQuery, setTopQuery] = useState(true);
const [dashboardQuery, setDashboardQuery] = useState(true);
const [loginQuery, setLoginQuery] = useState(true);
/**
* Convert the onSubmit callback to a promise function of the format
* expected by jsx/Modal.
Expand All @@ -39,20 +45,20 @@ function AdminQueryModal(props: {
reject();
return;
}
if (!topQuery && !dashboardQuery) {
if (!topQuery && !dashboardQuery && !loginQuery) {
swal.fire({
type: 'error',
text: 'Must pin as study query or pin to dashboard.',
text: 'Must pin as study query, to dashboard, or to the login page.',
});
reject();
return;
}
resolve([queryName.trim(), topQuery, dashboardQuery]);
resolve([queryName.trim(), topQuery, dashboardQuery, loginQuery]);
});
if (props.onSubmit) {
sbmt = sbmt.then((val: [string, boolean, boolean]) => {
const [name, topq, dashq] = val;
props.onSubmit(name, topq, dashq);
sbmt = sbmt.then((val: [string, boolean, boolean, boolean]) => {
const [name, topq, dashq, loginq] = val;
props.onSubmit(name, topq, dashq, loginq);
});
}
return sbmt;
Expand Down Expand Up @@ -87,6 +93,14 @@ function AdminQueryModal(props: {
setDashboardQuery(value)
}
/>
<CheckboxElement name='loginpage'
value={loginQuery}
label='Pin To Login Page'
onUserInput={
(name: string, value: boolean) =>
setLoginQuery(value)
}
/>
</FieldsetElement>
</form>
</Modal>;
Expand Down
37 changes: 19 additions & 18 deletions modules/dataquery/jsx/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,7 @@ function QueryList(props: {
const [fullQuery, setFullQuery]
= useState<boolean>(!props.defaultCollapsed);
const [unpinAdminQuery, setUnpinAdminQuery] = useState<number|null>(null);
const [adminPinAction, setAdminPinAction]
= useState<'top'|'dashboard'|'top,dashboard'>('top');
const [adminPinAction, setAdminPinAction] = useState<string>('');

useEffect(() => {
const modules = new Set<string>();
Expand Down Expand Up @@ -300,14 +299,17 @@ function QueryList(props: {
setAdminModalID(null);
setQueryName(null);

let param;
if (adminPinAction == 'top') {
param = 'adminname=' + encodeURIComponent(name);
} else if (adminPinAction == 'dashboard') {
param = 'dashboardname=' + encodeURIComponent(name);
} else if (adminPinAction == 'top,dashboard') {
param = 'adminname=' + encodeURIComponent(name)
+ '&dashboardname=' + encodeURIComponent(name);
let param = '';
if (adminPinAction.includes('top')) {
param += 'adminname=' + encodeURIComponent(name);
}
if (adminPinAction.includes('dashboard')) {
if (param != '') param += '&';
param += 'dashboardname=' + encodeURIComponent(name);
}
if (adminPinAction.includes('login')) {
if (param != '') param += '&';
param += 'loginname=' + encodeURIComponent(name);
}
fetch(
'/dataquery/queries/' + id
Expand Down Expand Up @@ -363,16 +365,15 @@ function QueryList(props: {
/>);
const adminModal = adminModalID == null ? '' :
<AdminQueryModal
onSubmit={(name, topQ, dashboardQ) => {
if (topQ && dashboardQ) {
setAdminPinAction('top,dashboard');
} else if (topQ) {
setAdminPinAction('top');
} else if (dashboardQ) {
setAdminPinAction('dashboard');
} else {
onSubmit={(name, topQ, dashboardQ, loginQ) => {
let boolList = '';
if (topQ) boolList += 'top';
if (dashboardQ) boolList += 'dashboard';
if (loginQ) boolList += 'login';
if (!topQ && !dashboardQ && !loginQ) {
throw new Error('Modal promise should not have resolved');
}
setAdminPinAction(boolList);
setQueryName(name);
}}
closeModal={() => setAdminModalID(null)}
Expand Down
14 changes: 14 additions & 0 deletions modules/dataquery/php/endpoints/queries/query.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ class Query extends \LORIS\Http\Endpoint
}
}

if (isset($params['loginname'])) {
if ($params['loginname'] == '') {
$query->removeAdminPinnedQuery('loginpage');
$resp['loginpage'] = 'Unpinned query from login page';
} else {
$query->setAdminPinnedQuery(
$user,
$params['loginname'],
'loginpage'
);
$resp['loginpage'] = 'Pinned query to login page';
}
}

if (isset($params['name'])) {
$query->setQueryName($user, $params['name']);
$resp['Name'] = 'Named query';
Expand Down
4 changes: 2 additions & 2 deletions modules/dataquery/php/provisioners/studyqueries.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class StudyQueries extends \LORIS\Data\Provisioners\DBRowProvisioner
* the pinned study queries.
*
* @param protected \LORIS\LorisInstance $loris - The LORIS object
* @param string $pintype - The pin type. Either
* "dashboard" or "study"
* @param string $pintype - The pin type. "dashboard"
* or "study" or "loginpage"
*/
function __construct(protected \LORIS\LorisInstance $loris, $pintype)
{
Expand Down
5 changes: 2 additions & 3 deletions modules/dataquery/php/query.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,6 @@ class Query implements \LORIS\StudyEntities\AccessibleResource,
'Name' => $name,
]
);

}

/**
Expand Down Expand Up @@ -492,7 +491,7 @@ class Query implements \LORIS\StudyEntities\AccessibleResource,
* @param string $name The name that the admin thinks should be
* used for the query
* @param string $type The type of querypin to remove. Either
* 'topquery' or 'dashboard'
* 'topquery' or 'dashboard' or 'loginpage'
*
* @return void
*/
Expand All @@ -517,7 +516,7 @@ class Query implements \LORIS\StudyEntities\AccessibleResource,
* Remove this query from the list of queries pinned by an admin.
*
* @param string $type The type of querypin to remove. Either
* 'topquery' or 'dashboard'
* 'topquery' or 'dashboard' or 'loginpage'
*
* @return void
*/
Expand Down
6 changes: 6 additions & 0 deletions modules/dataquery/static/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ paths:
description: The admin name to pin the query to the dashboard as. If the empty string, will be unpinned.
schema:
type: string
- name: loginpagename
in: query
style: pipeDelimited
description: The admin name to pin the query to the login page as. If the empty string, will be unpinned.
schema:
type: string
- name: name
in: query
style: pipeDelimited
Expand Down
8 changes: 4 additions & 4 deletions modules/dataquery/test/TestPlan.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
13. Click the `Pin` icon to pin some queries.
1. With and empty text in the `query name` text field, click the `Submit` button.
2. Assert that: the error message `Must provide a query name to pin query as.` is triggered.
3. Unchecking all checkboxes (i.e. `Pin Study Query` and `Pin Dashboard Summary`).
4. Assert that: clicking `Submit` triggers the error message `Must pin as study query or pin to dashboard.`.
3. Unchecking all checkboxes (i.e. `Pin Study Query` and `Pin Dashboard Summary` and `Pin to Login Page`).
4. Assert that: clicking `Submit` triggers the error message `Must pin as study query, to dashboard, or to the login page.`.
5. Check the `Pin Study Query` checkbox and click the submit button.
6. Assert that: the query is now pinned at the top of the page in the `Study Queries` panel.
7. Go to LORIS main page by clicking the `LORIS` name in the top-left corner.
Expand All @@ -34,8 +34,8 @@
13. Assert that: the query is displayed inside the right-side `Study Queries` panel.
14. Click the pinned query.
15. Assert that: the confirmation message `Query loaded` is displayed and query can immediately be executed.
16. Try pinning a query with both `Pin Study Query` and `Pin Dashboard Summary` options.
17. Assert that: both `Study Queries` in the dataquery module **AND** `Study Queries` in LORIS welcome page are displayed.
16. Try pinning a query with `Pin Study Query`, `Pin Dashboard Summary` and `Pin to Login Page` options.
17. Assert that: `Study Queries` in the dataquery module **AND** `Study Queries` in LORIS welcome page **AND** `Data in LORIS` on the LORIS Login Page are displayed.
14. Assert that: the query is now pinned at the top of the page, in `Study Queries` panel.
15. Go back to `LORIS main page`.
16. Assert that: `starred queries` are available in the right side `Starred Queries` panel.
Expand Down
9 changes: 9 additions & 0 deletions modules/login/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ to provide references on the LORIS landing page.
The configuration setting "StudyLogo" is the URL for an image to
show above the login box.

If a query is pinned to the login page from the dataquery module, then
after the tools/update_login_summary_statistics.php tool is run, a summary of
all the pinned queries will be displayed on the login page including the count.
If a query returns more than one row, the name will be appended with an 's'.
Pinned queries must include the column Project in order to be displayed.
Queries can also be added to project/tools/Login_Summary_Statistics to
be added to the calculations for the statistics on the login page.


## Interactions with LORIS

The login module redirects to the dashboard upon successful login.
Loading

0 comments on commit 1e706f2

Please sign in to comment.