From 11c5fe6ebb5aff421cc30762264de64432dcfa4b Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Fri, 20 Dec 2024 13:17:49 -0500 Subject: [PATCH 01/18] [Login] Summary Statistics --- SQL/0000-00-00-schema.sql | 1 + .../2024-12-18-Login_statistics.sql | 13 ++ .../configuration/php/configuration.class.inc | 2 + .../dataquery/jsx/welcome.adminquerymodal.tsx | 28 +++- modules/dataquery/jsx/welcome.tsx | 37 ++--- .../php/endpoints/queries/query.class.inc | 14 ++ .../php/provisioners/studyqueries.class.inc | 4 +- modules/dataquery/php/query.class.inc | 13 +- modules/dataquery/static/schema.yml | 6 + modules/dataquery/test/TestPlan.md | 8 +- modules/login/README.md | 7 + .../login/assets/summaryStatisticsPhone.js | 128 ++++++++++++++++++ modules/login/css/login.css | 25 ++++ modules/login/jsx/loginIndex.js | 27 +++- modules/login/jsx/summaryStatistics.js | 82 +++++++++++ .../login/php/summary_statistics.class.inc | 115 ++++++++++++++++ tools/update_login_summary_statistics.php | 118 ++++++++++++++++ 17 files changed, 592 insertions(+), 36 deletions(-) create mode 100644 SQL/New_patches/2024-12-18-Login_statistics.sql create mode 100644 modules/login/assets/summaryStatisticsPhone.js create mode 100644 modules/login/jsx/summaryStatistics.js create mode 100644 modules/login/php/summary_statistics.class.inc create mode 100644 tools/update_login_summary_statistics.php diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql index deabd1eff4f..a262e885be4 100644 --- a/SQL/0000-00-00-schema.sql +++ b/SQL/0000-00-00-schema.sql @@ -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; diff --git a/SQL/New_patches/2024-12-18-Login_statistics.sql b/SQL/New_patches/2024-12-18-Login_statistics.sql new file mode 100644 index 00000000000..99ec409531a --- /dev/null +++ b/SQL/New_patches/2024-12-18-Login_statistics.sql @@ -0,0 +1,13 @@ +CREATE TABLE Login_Summary_Statistics ( + Title VARCHAR(255), + Project VARCHAR(255), + Value INT +); + +ALTER TABLE Login_Summary_Statistics +MODIFY COLUMN PinType enum('topquery','dashboard', 'loginpage') DEFAULT NULL; + +ALTER TABLE Project +ADD COLUMN showSummaryOnLogin BOOLEAN DEFAULT TRUE; + +UPDATE Project SET showSummaryOnLogin = FALSE WHERE Name = 'DCP'; \ No newline at end of file diff --git a/modules/configuration/php/configuration.class.inc b/modules/configuration/php/configuration.class.inc index 06c2266f1b4..c6621b3f5c4 100644 --- a/modules/configuration/php/configuration.class.inc +++ b/modules/configuration/php/configuration.class.inc @@ -94,6 +94,8 @@ class Configuration extends \NDB_Form 'PatientID' => 'PatientID', 'PatientName' => 'PatientName', ]; + + $this->tpl_data['shared_queries'] = $user_starred_queries; } /** diff --git a/modules/dataquery/jsx/welcome.adminquerymodal.tsx b/modules/dataquery/jsx/welcome.adminquerymodal.tsx index 52350901a29..ff45d1c4bb4 100644 --- a/modules/dataquery/jsx/welcome.adminquerymodal.tsx +++ b/modules/dataquery/jsx/welcome.adminquerymodal.tsx @@ -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. @@ -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; @@ -87,6 +93,14 @@ function AdminQueryModal(props: { setDashboardQuery(value) } /> + + setLoginQuery(value) + } + /> ; diff --git a/modules/dataquery/jsx/welcome.tsx b/modules/dataquery/jsx/welcome.tsx index 40140048675..86103fcee4c 100644 --- a/modules/dataquery/jsx/welcome.tsx +++ b/modules/dataquery/jsx/welcome.tsx @@ -221,8 +221,7 @@ function QueryList(props: { const [fullQuery, setFullQuery] = useState(!props.defaultCollapsed); const [unpinAdminQuery, setUnpinAdminQuery] = useState(null); - const [adminPinAction, setAdminPinAction] - = useState<'top'|'dashboard'|'top,dashboard'>('top'); + const [adminPinAction, setAdminPinAction] = useState(''); useEffect(() => { const modules = new Set(); @@ -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 @@ -363,16 +365,15 @@ function QueryList(props: { />); const adminModal = adminModalID == null ? '' : { - 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)} diff --git a/modules/dataquery/php/endpoints/queries/query.class.inc b/modules/dataquery/php/endpoints/queries/query.class.inc index 221617ad9a3..5c8aee43486 100644 --- a/modules/dataquery/php/endpoints/queries/query.class.inc +++ b/modules/dataquery/php/endpoints/queries/query.class.inc @@ -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'; diff --git a/modules/dataquery/php/provisioners/studyqueries.class.inc b/modules/dataquery/php/provisioners/studyqueries.class.inc index fab6712570c..e00c74aff13 100644 --- a/modules/dataquery/php/provisioners/studyqueries.class.inc +++ b/modules/dataquery/php/provisioners/studyqueries.class.inc @@ -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) { diff --git a/modules/dataquery/php/query.class.inc b/modules/dataquery/php/query.class.inc index 1a610e516cc..cdece610a50 100644 --- a/modules/dataquery/php/query.class.inc +++ b/modules/dataquery/php/query.class.inc @@ -366,7 +366,16 @@ class Query implements \LORIS\StudyEntities\AccessibleResource, 'Name' => $name, ] ); + } + /** + * Get the name of the query. + * + * @return void + */ + public function getQueryName() + { + return $this->name; } /** @@ -492,7 +501,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 */ @@ -517,7 +526,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 */ diff --git a/modules/dataquery/static/schema.yml b/modules/dataquery/static/schema.yml index 59a1e70067b..d23983c5d6b 100644 --- a/modules/dataquery/static/schema.yml +++ b/modules/dataquery/static/schema.yml @@ -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 diff --git a/modules/dataquery/test/TestPlan.md b/modules/dataquery/test/TestPlan.md index bf919a98b8b..ba9d19aef72 100644 --- a/modules/dataquery/test/TestPlan.md +++ b/modules/dataquery/test/TestPlan.md @@ -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. @@ -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. diff --git a/modules/login/README.md b/modules/login/README.md index 78d20c8396e..9956e4413c2 100644 --- a/modules/login/README.md +++ b/modules/login/README.md @@ -38,6 +38,13 @@ 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. + + ## Interactions with LORIS The login module redirects to the dashboard upon successful login. diff --git a/modules/login/assets/summaryStatisticsPhone.js b/modules/login/assets/summaryStatisticsPhone.js new file mode 100644 index 00000000000..36d5c30cd09 --- /dev/null +++ b/modules/login/assets/summaryStatisticsPhone.js @@ -0,0 +1,128 @@ +import * as React from "react" +const SvgComponent = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) +export default SvgComponent \ No newline at end of file diff --git a/modules/login/css/login.css b/modules/login/css/login.css index db93807a816..c3395a31caa 100644 --- a/modules/login/css/login.css +++ b/modules/login/css/login.css @@ -4,3 +4,28 @@ cursor: pointer; color: grey; } + +.study-description { + display: flex; + flex-direction: row; +} + +.stats-phone { + position: relative; + width: 300px; + margin: auto; +} + +.stats-screen { + position: absolute; + left: 8%; + top: 10%; + width: 55%; + height: 60%; +} + +@media screen and (max-width: 768px) { + .study-description { + flex-direction: column-reverse; + } +} \ No newline at end of file diff --git a/modules/login/jsx/loginIndex.js b/modules/login/jsx/loginIndex.js index b0f6c254e0e..c9480e74d24 100644 --- a/modules/login/jsx/loginIndex.js +++ b/modules/login/jsx/loginIndex.js @@ -14,6 +14,7 @@ import { PasswordElement, ButtonElement, } from 'jsx/Form'; +import SummaryStatistics from './summaryStatistics'; /** * Login form. @@ -53,6 +54,7 @@ class Login extends Component { requestAccount: null, expiredPassword: null, }, + summaryStatistics: null, isLoaded: false, }; // Bind component instance to custom methods @@ -68,7 +70,6 @@ class Login extends Component { */ componentDidMount() { this.fetchData() - .then(() => this.setState({isLoaded: true})); } /** @@ -91,8 +92,18 @@ class Login extends Component { // request account setup. state.component.requestAccount = json.requestAccount; state.oidc = json.oidc; - state.isLoaded = true; this.setState(state); + }).then(() => { + fetch(window.location.origin + '/login/summary_statistics', { + method: 'GET', + }) + .then((resp) => resp.json()) + .then((json) => { + this.setState({ + summaryStatistics: json, + isLoaded: true, + }); + }); }).catch((error) => { this.setState({error: true}); console.error(error); @@ -274,9 +285,19 @@ class Login extends Component { collapsing={false} bold={true} > - {study} +
+ { + this.state.summaryStatistics + && + } + {study} +
+
+
); diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js new file mode 100644 index 00000000000..12fcbaad406 --- /dev/null +++ b/modules/login/jsx/summaryStatistics.js @@ -0,0 +1,82 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {SelectElement} from 'jsx/Form'; +import SummaryStatisticsPhone from '../assets/summaryStatisticsPhone.js'; + +/** + * Login Summary Statistics + * + * + * @author Saagar Arya + * @version 1.0.0 + */ +class SummaryStatistics extends Component { + /** + * @constructor + * @param {object} props - React Component properties + */ + constructor(props) { + super(props); + + this.state = { + selectedProject: 'All Projects', + data: props.data, + }; + } + + /** + * Renders the React component. + * + * @return {JSX} - React markup for the component + */ + render() { + return ( +
+ + {/* Phone screen */} +
+ {/* Project Selector */} + { + this.setState({ + selectedProject: value, + }); + }} + emptyOption={false} + /> +
Data in LORIS:
+ {/* Statistics */} + {(this.state.data.statistics).map((statistic) => { + if ( + statistic.Project + == this.state.data.projects[this.state.selectedProject] + && statistic.Value > 0 + ) { + return
+ + {statistic.Value.toLocaleString('fr-CA')}{' '} + + {statistic.Title}{statistic.Value != 1 ? 's' : ''} +
; + } + })} +
+
+ ); + } +} + +SummaryStatistics.propTypes = { + DataURL: PropTypes.string.isRequired, + data: PropTypes.object, +}; + +export default SummaryStatistics; diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc new file mode 100644 index 00000000000..159ec8306ba --- /dev/null +++ b/modules/login/php/summary_statistics.class.inc @@ -0,0 +1,115 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +class Summary_Statistics extends \NDB_Page implements ETagCalculator +{ + /** + * This function will return a json object for login summary statistics module. + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + public function handle(ServerRequestInterface $request) : ResponseInterface + { + // Ensure GET or POST request. + switch ($request->getMethod()) { + case 'GET': + return $this->_handleGET($request); + default: + return new \LORIS\Http\Response\JSON\MethodNotAllowed( + $this->allowedMethods() + ); + } + } + + /** + * Initialize setup of the login module. + * + * @param ServerRequestInterface $request The incoming PSR7 request. + * + * @return ResponseInterface The outgoing PSR7 response + */ + private function _handleGET(ServerRequestInterface $request) : ResponseInterface + { + $DB = $this->loris->getDatabaseConnection(); + $response = $DB->pselect( + "SELECT * FROM Login_Summary_Statistics + WHERE Value > 0", + [], + ); + if (count($response) == 0) { + return new \LORIS\Http\Response\JsonResponse(null); + } + $projects = $DB->pselectCol( + "SELECT Name FROM Project + WHERE showSummaryOnLogin='1' + ", + [] + ); + + if (count($projects) > 1) { + $projects = array_merge(['All Projects' => 'All Projects'], $projects); + } + + return new \LORIS\Http\Response\JsonResponse( + [ + 'statistics' => iterator_to_array($response), + 'projects' => $projects, + ] + ); + } + + /** + * An ETagCalculator provides the ability to calculate an ETag for + * an incoming HTTP request. + * + * @param ServerRequestInterface $request The incoming PSR7 request. + * + * @return string The value to use for the ETag header. + */ + public function ETag(ServerRequestInterface $request): string + { + return md5(json_encode((string) $this->_handleGET($request)->getBody())); + } + + /** + * Return an array of valid HTTP methods for this endpoint + * + * @return string[] Valid versions + */ + protected function allowedMethods(): array + { + return [ + 'GET', + ]; + } + + /** + * Returns true if the user has permission to access + * the Login module + * + * @param \User $user The user whose access is being checked + * + * @return bool true if user has permission + */ + function _hasAccess(\User $user) : bool + { + return true; + } + +} diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php new file mode 100644 index 00000000000..e28bbedf0f9 --- /dev/null +++ b/tools/update_login_summary_statistics.php @@ -0,0 +1,118 @@ +pselectCol( + "SELECT Name FROM Project + WHERE showSummaryOnLogin='1'", + [] +); + +// TODO: delete. This would return a query object instead of +// queryIDs and it was really difficult to try to get it to run +// $provisioner = ( +// new \LORIS\dataquery\Provisioners\StudyQueries($lorisInstance, 'loginpage') +// ); +$pinnedqueries = $DB->pselectWithIndexKey( + "SELECT dq.QueryID, Query, dsq.Name + FROM dataquery_queries dq + LEFT JOIN dataquery_study_queries_rel dsq ON + (dq.QueryID=dsq.QueryID) + WHERE dsq.PinType=:pintype + GROUP BY QueryID, Query, dsq.Name + ORDER BY QueryID", + ['pintype' => 'loginpage'], + 'QueryID' +); +$user = (new \User())->factory('admin'); +$data = ['All Projects' => []]; +$DB->run("DELETE FROM Login_Summary_Statistics", []); +foreach ($projects as $project) { + $data[$project] = []; +} +foreach ($pinnedqueries as $pin) { + $queryInProjects = []; + $queryID = $pin['QueryID']; + $queryName = $pin['Name']; + + // TODO: delete. Commented out after changing from + // hardcoded queryID to pinned queries + // Get the query Name + // $query = new \LORIS\dataquery\Query($lorisInstance, $queryID); + // $query->loadQueryMetadata($user); + // $queryName = $query->getQueryName(); + + // Initialize the count at 0 + $data['All Projects'][$queryName] = 0; + foreach ($projects as $project) { + $data[$project][$queryName] = 0; + } + + // Get the data of the query + $runner = new \LORIS\dataquery\endpoints\Queries\query\Run( + $lorisInstance, + $queryID + ); + + $response = $runner->runQuery($user, $queryID)->getBody(); + $result = []; + while ($response->eof() == false) { + $next = $response->read(1024); + if (!empty($next)) { + // TODO: for some reason the last one which + // is empty gets added too so it is + 1 for All Projects + // TODO: add as a OBJECT not string somehow + $result[] = $next; + $inAProject = false; + // TODO: this processes as string by looking for the project name. + // Change to something better when it is an object. + foreach ($projects as $project) { + if (strpos($next, $project) !== false) { + $data[$project][$queryName] += 1; + $inAProject = true; + $queryInProjects[$project] = $project; + } + } + // Only add to All Projects for projects that are included in the query + if ($inAProject) { + $data['All Projects'][$queryName] += 1; + } + } + } + // If the query only has data in one project, remove from the All Projects tab + if (count($queryInProjects) == 1) { + unset($data['All Projects'][$queryName]); + } else if (count($queryInProjects) == 0) { + print_r( + "--- ERROR: Query $queryName has no data in any project. " + . "Please delete this query, and re-save with the Project column selected.\n" + ); + } +} + +print_r($data); +foreach ($data as $project => $values) { + foreach ($values as $title => $value) { + $DB->insertOnDuplicateUpdate( + 'Login_Summary_Statistics', + [ + 'Project' => $project, + 'Title' => $title, + 'value' => $value + ] + ); + } +} \ No newline at end of file From 8d5dedc073633685481dbf9a7b08eb3acaf1dd57 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Fri, 20 Dec 2024 14:13:57 -0500 Subject: [PATCH 02/18] Add SQL File ability --- tools/update_login_summary_statistics.php | 58 +++++++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index e28bbedf0f9..e3b407fa31f 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -1,5 +1,35 @@ + * @license Loris license + * @link https://www.github.com/aces/Loris/ + */ + require_once "generic_includes.php"; require_once __DIR__ . "/../modules/dataquery/php/query.class.inc"; @@ -55,8 +85,8 @@ // $query->loadQueryMetadata($user); // $queryName = $query->getQueryName(); - // Initialize the count at 0 - $data['All Projects'][$queryName] = 0; + // Initialize the counts + $data['All Projects'][$queryName] = -1; foreach ($projects as $project) { $data[$project][$queryName] = 0; } @@ -72,8 +102,6 @@ while ($response->eof() == false) { $next = $response->read(1024); if (!empty($next)) { - // TODO: for some reason the last one which - // is empty gets added too so it is + 1 for All Projects // TODO: add as a OBJECT not string somehow $result[] = $next; $inAProject = false; @@ -86,7 +114,7 @@ $queryInProjects[$project] = $project; } } - // Only add to All Projects for projects that are included in the query + // Only add to All Projects if the query is in at least one project if ($inAProject) { $data['All Projects'][$queryName] += 1; } @@ -103,6 +131,26 @@ } } +// Get SQL queries from ../project/tools/Login_Summary_Statistics +// Filename should be the desired name of the query +// TODO: How could we order them? +$folder = __DIR__ . "/../project/tools/Login_Summary_Statistics/"; +if (!is_dir($folder)) { + print_r("Folder $folder does not exist\n"); +} else { + $files = scandir($folder); + foreach ($files as $file) { + if (is_file($folder . $file)) { + print_r("Reading SQL File $file\n"); + $result = $DB->pselect(file_get_contents($folder . $file), []); + $queryName = pathinfo($file, PATHINFO_FILENAME); + foreach ($result as $row) { + $data[$row['ProjectName']][$queryName] = $row['count']; + } + } + } +} + print_r($data); foreach ($data as $project => $values) { foreach ($values as $title => $value) { From ddf26882d902d4d9c6776fa04b83c523af280496 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 8 Jan 2025 11:19:31 -0500 Subject: [PATCH 03/18] Add default queries --- SQL/0000-00-00-schema.sql | 2 +- .../01_Female Participant.sql | 9 ++++++ .../02_Male Participant.sql | 9 ++++++ SQL/Login_Summary_Statistics/03_Site.sql | 8 +++++ SQL/Login_Summary_Statistics/04_Visit.sql | 7 +++++ .../05_Instrument.sql | 8 +++++ SQL/Login_Summary_Statistics/06_Scan.sql | 8 +++++ .../2024-12-18-Login_statistics.sql | 3 +- tools/update_login_summary_statistics.php | 31 ++++++++++--------- 9 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 SQL/Login_Summary_Statistics/01_Female Participant.sql create mode 100644 SQL/Login_Summary_Statistics/02_Male Participant.sql create mode 100644 SQL/Login_Summary_Statistics/03_Site.sql create mode 100644 SQL/Login_Summary_Statistics/04_Visit.sql create mode 100644 SQL/Login_Summary_Statistics/05_Instrument.sql create mode 100644 SQL/Login_Summary_Statistics/06_Scan.sql diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql index a262e885be4..f004f157f18 100644 --- a/SQL/0000-00-00-schema.sql +++ b/SQL/0000-00-00-schema.sql @@ -7,7 +7,7 @@ CREATE TABLE `Project` ( `Name` VARCHAR(255) NOT NULL, `Alias` char(4) NOT NULL, `recruitmentTarget` INT(6) Default NULL, - `showSummaryOnLogin` BOOLEAN DEFAULT TRUE; + `showSummaryOnLogin` BOOLEAN DEFAULT TRUE, PRIMARY KEY (`ProjectID`), UNIQUE KEY `u_ProjectName` (`Name`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; diff --git a/SQL/Login_Summary_Statistics/01_Female Participant.sql b/SQL/Login_Summary_Statistics/01_Female Participant.sql new file mode 100644 index 00000000000..4caf299cce0 --- /dev/null +++ b/SQL/Login_Summary_Statistics/01_Female Participant.sql @@ -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 \ No newline at end of file diff --git a/SQL/Login_Summary_Statistics/02_Male Participant.sql b/SQL/Login_Summary_Statistics/02_Male Participant.sql new file mode 100644 index 00000000000..410524301de --- /dev/null +++ b/SQL/Login_Summary_Statistics/02_Male Participant.sql @@ -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 \ No newline at end of file diff --git a/SQL/Login_Summary_Statistics/03_Site.sql b/SQL/Login_Summary_Statistics/03_Site.sql new file mode 100644 index 00000000000..6216b41c69e --- /dev/null +++ b/SQL/Login_Summary_Statistics/03_Site.sql @@ -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 \ No newline at end of file diff --git a/SQL/Login_Summary_Statistics/04_Visit.sql b/SQL/Login_Summary_Statistics/04_Visit.sql new file mode 100644 index 00000000000..a1fe4b0fd88 --- /dev/null +++ b/SQL/Login_Summary_Statistics/04_Visit.sql @@ -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 \ No newline at end of file diff --git a/SQL/Login_Summary_Statistics/05_Instrument.sql b/SQL/Login_Summary_Statistics/05_Instrument.sql new file mode 100644 index 00000000000..cc4fe0edc86 --- /dev/null +++ b/SQL/Login_Summary_Statistics/05_Instrument.sql @@ -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; \ No newline at end of file diff --git a/SQL/Login_Summary_Statistics/06_Scan.sql b/SQL/Login_Summary_Statistics/06_Scan.sql new file mode 100644 index 00000000000..423169f8c57 --- /dev/null +++ b/SQL/Login_Summary_Statistics/06_Scan.sql @@ -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; \ No newline at end of file diff --git a/SQL/New_patches/2024-12-18-Login_statistics.sql b/SQL/New_patches/2024-12-18-Login_statistics.sql index 99ec409531a..21beac55af6 100644 --- a/SQL/New_patches/2024-12-18-Login_statistics.sql +++ b/SQL/New_patches/2024-12-18-Login_statistics.sql @@ -1,10 +1,11 @@ +DROP TABLE IF EXISTS Login_Summary_Statistics; CREATE TABLE Login_Summary_Statistics ( Title VARCHAR(255), Project VARCHAR(255), Value INT ); -ALTER TABLE Login_Summary_Statistics +ALTER TABLE dataquery_study_queries_rel MODIFY COLUMN PinType enum('topquery','dashboard', 'loginpage') DEFAULT NULL; ALTER TABLE Project diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index e3b407fa31f..0d6cbe62ec1 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -131,22 +131,25 @@ } } -// Get SQL queries from ../project/tools/Login_Summary_Statistics -// Filename should be the desired name of the query -// TODO: How could we order them? +// Try to get SQL queries from ../project/tools/Login_Summary_Statistics +// Otherwise get them from SQL/Login_Summary_Statistics +// Filename should be the desired name of the query, with a "#_" infront +// Example: 01_Site.sql where 01 means it is the first query $folder = __DIR__ . "/../project/tools/Login_Summary_Statistics/"; if (!is_dir($folder)) { - print_r("Folder $folder does not exist\n"); -} else { - $files = scandir($folder); - foreach ($files as $file) { - if (is_file($folder . $file)) { - print_r("Reading SQL File $file\n"); - $result = $DB->pselect(file_get_contents($folder . $file), []); - $queryName = pathinfo($file, PATHINFO_FILENAME); - foreach ($result as $row) { - $data[$row['ProjectName']][$queryName] = $row['count']; - } + print_r("Folder $folder does not exist, using ../SQL/Login_Summary_Statistics instead \n"); + $folder = __DIR__ . "/../SQL/Login_Summary_Statistics/"; +} + +$files = scandir($folder); +foreach ($files as $file) { + if (is_file($folder . $file)) { + print_r("Reading SQL File $file\n"); + $result = $DB->pselect(file_get_contents($folder . $file), []); + $queryName = pathinfo($file, PATHINFO_FILENAME); + $queryName = explode("_", $queryName)[1]; + foreach ($result as $row) { + $data[$row['ProjectName']][$queryName] = $row['count']; } } } From 72b13f0db59480c21d00bfb217abf70d7c49b05c Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 8 Jan 2025 12:06:43 -0500 Subject: [PATCH 04/18] Add export as csv --- modules/login/jsx/summaryStatistics.js | 11 ++++++++++- modules/login/php/summary_statistics.class.inc | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js index 12fcbaad406..6c6559e816e 100644 --- a/modules/login/jsx/summaryStatistics.js +++ b/modules/login/jsx/summaryStatistics.js @@ -47,7 +47,16 @@ class SummaryStatistics extends Component { }} emptyOption={false} /> -
Data in LORIS:
+
+ { + navigator.clipboard.writeText(this.state.data.csv); + }} + > + Data + + {' '}in LORIS: +
{/* Statistics */} {(this.state.data.statistics).map((statistic) => { if ( diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 159ec8306ba..0d7e185fcaf 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -66,10 +66,18 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator $projects = array_merge(['All Projects' => 'All Projects'], $projects); } + // csv version for exporting to clipboard + $reusable_response = iterator_to_array($response); + $csv = "Project,Statistic,Value\n"; + foreach ($reusable_response as $row) { + $csv .= $row['Project'] . "," . $row['Title'] . "," . $row['Value'] . "\n"; + } + return new \LORIS\Http\Response\JsonResponse( [ - 'statistics' => iterator_to_array($response), + 'statistics' => $reusable_response, 'projects' => $projects, + 'csv' => $csv, ] ); } From 12daffbf32bb6d8c2f8f0f624cd26fabee451dda Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 8 Jan 2025 12:16:59 -0500 Subject: [PATCH 05/18] Fix php formatting --- .../login/php/summary_statistics.class.inc | 2 +- tools/update_login_summary_statistics.php | 26 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 0d7e185fcaf..48e6ac23b61 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -70,7 +70,7 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator $reusable_response = iterator_to_array($response); $csv = "Project,Statistic,Value\n"; foreach ($reusable_response as $row) { - $csv .= $row['Project'] . "," . $row['Title'] . "," . $row['Value'] . "\n"; + $csv .= $row['Project'] .",". $row['Title'] .",". $row['Value'] . "\n"; } return new \LORIS\Http\Response\JsonResponse( diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index 0d6cbe62ec1..3662161cbc5 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -3,14 +3,15 @@ /** * This script updates the Login Summary Statistics table in the database * based on the queries that are pinned to the login page in the dataquery module, - * and on the SQL queries in the project/tools/Login_Summary_Statistics folder. - * Pinned DQT Queries must include the Project column. SQL query files must return - * a result with columns Project.Name and count. The name of the file must be the desired - * name of the query. Here is an example SQL query that counts the number of sites: - * + * and on the SQL queries in the project/tools/Login_Summary_Statistics folder, + * or if that does not exist, the SQL/Login_Summary_Statistics folder. + * Pinned DQT Queries must include the Project column. SQL query files must return a + * result with columns ProjectName and count. Here is an example SQL query that + * counts the number of sites: + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * SELECT - * IFNULL(Project.Name, 'All Projects'), + * SELECT + * IFNULL(Project.Name, 'All Projects') as ProjectName, * COUNT(DISTINCT CenterID) AS count * FROM psc * JOIN session s ON s.CenterID = psc.CenterID @@ -125,8 +126,8 @@ unset($data['All Projects'][$queryName]); } else if (count($queryInProjects) == 0) { print_r( - "--- ERROR: Query $queryName has no data in any project. " - . "Please delete this query, and re-save with the Project column selected.\n" + "--- ERROR: Query $queryName has no data in any project. Please delete " + . "this query, and re-save with the Project column selected.\n" ); } } @@ -137,7 +138,10 @@ // Example: 01_Site.sql where 01 means it is the first query $folder = __DIR__ . "/../project/tools/Login_Summary_Statistics/"; if (!is_dir($folder)) { - print_r("Folder $folder does not exist, using ../SQL/Login_Summary_Statistics instead \n"); + print_r( + "Folder $folder does not exist, " + . "using ../SQL/Login_Summary_Statistics instead \n" + ); $folder = __DIR__ . "/../SQL/Login_Summary_Statistics/"; } @@ -145,7 +149,7 @@ foreach ($files as $file) { if (is_file($folder . $file)) { print_r("Reading SQL File $file\n"); - $result = $DB->pselect(file_get_contents($folder . $file), []); + $result = $DB->pselect(file_get_contents($folder . $file), []); $queryName = pathinfo($file, PATHINFO_FILENAME); $queryName = explode("_", $queryName)[1]; foreach ($result as $row) { From f3ea9d70aa370a8a06417f79736959aacfb79a24 Mon Sep 17 00:00:00 2001 From: Saagar Arya <51128536+skarya22@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:25:51 -0800 Subject: [PATCH 06/18] Add ordering --- SQL/New_patches/2024-12-18-Login_statistics.sql | 4 +++- modules/login/php/summary_statistics.class.inc | 3 ++- tools/update_login_summary_statistics.php | 11 +++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/SQL/New_patches/2024-12-18-Login_statistics.sql b/SQL/New_patches/2024-12-18-Login_statistics.sql index 21beac55af6..06a307e3fd4 100644 --- a/SQL/New_patches/2024-12-18-Login_statistics.sql +++ b/SQL/New_patches/2024-12-18-Login_statistics.sql @@ -2,7 +2,9 @@ DROP TABLE IF EXISTS Login_Summary_Statistics; CREATE TABLE Login_Summary_Statistics ( Title VARCHAR(255), Project VARCHAR(255), - Value INT + Value INT, + QueryOrder INT, + PRIMARY KEY (Title, Project) ); ALTER TABLE dataquery_study_queries_rel diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 48e6ac23b61..299b4ba1c40 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -49,7 +49,8 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator $DB = $this->loris->getDatabaseConnection(); $response = $DB->pselect( "SELECT * FROM Login_Summary_Statistics - WHERE Value > 0", + WHERE Value > 0 + ORDER BY QueryOrder ASC", [], ); if (count($response) == 0) { diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index 3662161cbc5..9915aa96d61 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -74,11 +74,14 @@ foreach ($projects as $project) { $data[$project] = []; } +$order = 0; +$queryToOrder = []; foreach ($pinnedqueries as $pin) { $queryInProjects = []; $queryID = $pin['QueryID']; $queryName = $pin['Name']; - + $queryToOrder[$queryName] = $order; + $order++; // TODO: delete. Commented out after changing from // hardcoded queryID to pinned queries // Get the query Name @@ -130,6 +133,7 @@ . "this query, and re-save with the Project column selected.\n" ); } + $order++; } // Try to get SQL queries from ../project/tools/Login_Summary_Statistics @@ -151,7 +155,9 @@ print_r("Reading SQL File $file\n"); $result = $DB->pselect(file_get_contents($folder . $file), []); $queryName = pathinfo($file, PATHINFO_FILENAME); + $queryOrder = explode("_", $queryName)[0]; $queryName = explode("_", $queryName)[1]; + $queryToOrder[$queryName] = $queryOrder; foreach ($result as $row) { $data[$row['ProjectName']][$queryName] = $row['count']; } @@ -166,7 +172,8 @@ [ 'Project' => $project, 'Title' => $title, - 'value' => $value + 'value' => $value, + 'QueryOrder' => $queryToOrder[$title] ] ); } From 8120431ce87d6c6899cc466c030477da0814b2c3 Mon Sep 17 00:00:00 2001 From: Saagar Arya <51128536+skarya22@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:55:04 -0800 Subject: [PATCH 07/18] Edge cases for project names --- modules/login/jsx/summaryStatistics.js | 4 +++- modules/login/php/summary_statistics.class.inc | 14 ++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js index 6c6559e816e..f61f91bad38 100644 --- a/modules/login/jsx/summaryStatistics.js +++ b/modules/login/jsx/summaryStatistics.js @@ -19,7 +19,9 @@ class SummaryStatistics extends Component { super(props); this.state = { - selectedProject: 'All Projects', + selectedProject: props.data.projects.includes('All Projects') + ? props.data.projects.indexOf('All Projects') + : '0', data: props.data, }; } diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 299b4ba1c40..26d7784be31 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -57,16 +57,10 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator return new \LORIS\Http\Response\JsonResponse(null); } $projects = $DB->pselectCol( - "SELECT Name FROM Project - WHERE showSummaryOnLogin='1' - ", + "SELECT DISTINCT Project FROM Login_Summary_Statistics", [] ); - if (count($projects) > 1) { - $projects = array_merge(['All Projects' => 'All Projects'], $projects); - } - // csv version for exporting to clipboard $reusable_response = iterator_to_array($response); $csv = "Project,Statistic,Value\n"; @@ -76,9 +70,9 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator return new \LORIS\Http\Response\JsonResponse( [ - 'statistics' => $reusable_response, - 'projects' => $projects, - 'csv' => $csv, + 'statistics'. => $reusable_response, + 'projects' => $projects, + 'csv' => $csv, ] ); } From 3d7d972c1cd06567dce312ce046dc9f224b3e0d9 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Fri, 17 Jan 2025 13:56:43 -0500 Subject: [PATCH 08/18] Add EEG --- .../07_EEG Recording.sql | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 SQL/Login_Summary_Statistics/07_EEG Recording.sql diff --git a/SQL/Login_Summary_Statistics/07_EEG Recording.sql b/SQL/Login_Summary_Statistics/07_EEG Recording.sql new file mode 100644 index 00000000000..0afa65e4713 --- /dev/null +++ b/SQL/Login_Summary_Statistics/07_EEG Recording.sql @@ -0,0 +1,17 @@ +SELECT + IFNULL(Project.Name, 'All Projects') as ProjectName, + COUNT(PhysiologicalFileID) AS count +FROM physiological_parameter_file +LEFT JOIN physiological_file USING (PhysiologicalFileID) +LEFT JOIN physiological_output_type USING (PhysiologicalOutputTypeID) +LEFT JOIN Project ON physiological_parameter_file.ProjectID = Project.ProjectID +WHERE ( + ParameterTypeID = ( + SELECT ParameterTypeID + FROM parameter_type + WHERE Name = 'RecordingDuration' + ) +) +AND OutputTypeName = 'raw' +-- AND Project.showSummaryOnLogin = 1 +GROUP BY ProjectName WITH ROLLUP \ No newline at end of file From ac13cf7698d765fc850639d300403f72a69ae32a Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 17:59:57 -0500 Subject: [PATCH 09/18] Cleanup --- modules/configuration/php/configuration.class.inc | 2 -- modules/login/php/summary_statistics.class.inc | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/configuration/php/configuration.class.inc b/modules/configuration/php/configuration.class.inc index b85e7cfa124..576b737f4de 100644 --- a/modules/configuration/php/configuration.class.inc +++ b/modules/configuration/php/configuration.class.inc @@ -93,8 +93,6 @@ class Configuration extends \NDB_Form 'PatientID' => 'PatientID', 'PatientName' => 'PatientName', ]; - - $this->tpl_data['shared_queries'] = $user_starred_queries; } /** diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 26d7784be31..cf640f2eac1 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -70,9 +70,9 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator return new \LORIS\Http\Response\JsonResponse( [ - 'statistics'. => $reusable_response, - 'projects' => $projects, - 'csv' => $csv, + 'statistics' => $reusable_response, + 'projects' => $projects, + 'csv' => $csv, ] ); } From ae8f6454c1ffb2d5a4de210f42da0beed132acc7 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 19:06:59 -0500 Subject: [PATCH 10/18] Try to fix LORIS Integration login test --- .../LorisIntegrationTest.class.inc | 4 ++-- tools/update_login_summary_statistics.php | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/integrationtests/LorisIntegrationTest.class.inc b/test/integrationtests/LorisIntegrationTest.class.inc index 58ab81b4756..f8cb4ffcccb 100755 --- a/test/integrationtests/LorisIntegrationTest.class.inc +++ b/test/integrationtests/LorisIntegrationTest.class.inc @@ -171,13 +171,13 @@ abstract class LorisIntegrationTest extends TestCase protected function login($username, $password) { $this->safeGet($this->url); - /* + $this->webDriver->wait(120, 1000)->until( WebDriverExpectedCondition::presenceOfElementLocated( WebDriverBy::Name("username") ) ); - */ + $usernameEl = $this->safeFindElement( WebDriverBy::Name("username") ); diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index 9915aa96d61..de68ac9e542 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -74,7 +74,7 @@ foreach ($projects as $project) { $data[$project] = []; } -$order = 0; +$order = 0; $queryToOrder = []; foreach ($pinnedqueries as $pin) { $queryInProjects = []; @@ -153,10 +153,10 @@ foreach ($files as $file) { if (is_file($folder . $file)) { print_r("Reading SQL File $file\n"); - $result = $DB->pselect(file_get_contents($folder . $file), []); - $queryName = pathinfo($file, PATHINFO_FILENAME); + $result = $DB->pselect(file_get_contents($folder . $file), []); + $queryName = pathinfo($file, PATHINFO_FILENAME); $queryOrder = explode("_", $queryName)[0]; - $queryName = explode("_", $queryName)[1]; + $queryName = explode("_", $queryName)[1]; $queryToOrder[$queryName] = $queryOrder; foreach ($result as $row) { $data[$row['ProjectName']][$queryName] = $row['count']; @@ -170,9 +170,9 @@ $DB->insertOnDuplicateUpdate( 'Login_Summary_Statistics', [ - 'Project' => $project, - 'Title' => $title, - 'value' => $value, + 'Project' => $project, + 'Title' => $title, + 'value' => $value, 'QueryOrder' => $queryToOrder[$title] ] ); From c465451a0079ba55f89b4714521f5a28f47e7bcb Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 19:28:25 -0500 Subject: [PATCH 11/18] Attempt to fix integration test loading of login page again --- SQL/0000-00-00-schema.sql | 12 +++- modules/login/jsx/loginIndex.js | 2 +- .../login/php/summary_statistics.class.inc | 72 ++++++++----------- .../LorisIntegrationTest.class.inc | 4 +- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql index acb0e63870a..13da9e2f7fc 100644 --- a/SQL/0000-00-00-schema.sql +++ b/SQL/0000-00-00-schema.sql @@ -117,8 +117,6 @@ CREATE TABLE `users` ( CONSTRAINT `FK_users_2` FOREIGN KEY (`language_preference`) REFERENCES `language` (`language_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - - INSERT INTO `users` (ID,UserID,Real_name,First_name,Last_name,Email,Privilege,PSCPI,DBAccess,Active,Pending_approval,PasswordChangeRequired) VALUES (1,'admin','Admin account','Admin','account','admin@example.com',0,'N','','Y','N',0); @@ -2498,6 +2496,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, @@ -2557,7 +2563,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) diff --git a/modules/login/jsx/loginIndex.js b/modules/login/jsx/loginIndex.js index c9480e74d24..26b4bce3e03 100644 --- a/modules/login/jsx/loginIndex.js +++ b/modules/login/jsx/loginIndex.js @@ -69,7 +69,7 @@ class Login extends Component { * Executes after component mounts. */ componentDidMount() { - this.fetchData() + this.fetchData(); } /** diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index cf640f2eac1..894ee6e62ca 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -29,7 +29,35 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator // Ensure GET or POST request. switch ($request->getMethod()) { case 'GET': - return $this->_handleGET($request); + $DB = $this->loris->getDatabaseConnection(); + $response = $DB->pselect( + "SELECT * FROM Login_Summary_Statistics + WHERE Value > 0 + ORDER BY QueryOrder ASC", + [], + ); + if (count($response) == 0) { + return new \LORIS\Http\Response\JsonResponse(null); + } + $projects = $DB->pselectCol( + "SELECT DISTINCT Project FROM Login_Summary_Statistics", + [] + ); + + // csv version for exporting to clipboard + $reusable_response = iterator_to_array($response); + $csv = "Project,Statistic,Value\n"; + foreach ($reusable_response as $row) { + $csv .= $row['Project'] .",". $row['Title'] .",". $row['Value'] . "\n"; + } + + return new \LORIS\Http\Response\JsonResponse( + [ + 'statistics' => $reusable_response, + 'projects' => $projects, + 'csv' => $csv, + ] + ); default: return new \LORIS\Http\Response\JSON\MethodNotAllowed( $this->allowedMethods() @@ -37,46 +65,6 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator } } - /** - * Initialize setup of the login module. - * - * @param ServerRequestInterface $request The incoming PSR7 request. - * - * @return ResponseInterface The outgoing PSR7 response - */ - private function _handleGET(ServerRequestInterface $request) : ResponseInterface - { - $DB = $this->loris->getDatabaseConnection(); - $response = $DB->pselect( - "SELECT * FROM Login_Summary_Statistics - WHERE Value > 0 - ORDER BY QueryOrder ASC", - [], - ); - if (count($response) == 0) { - return new \LORIS\Http\Response\JsonResponse(null); - } - $projects = $DB->pselectCol( - "SELECT DISTINCT Project FROM Login_Summary_Statistics", - [] - ); - - // csv version for exporting to clipboard - $reusable_response = iterator_to_array($response); - $csv = "Project,Statistic,Value\n"; - foreach ($reusable_response as $row) { - $csv .= $row['Project'] .",". $row['Title'] .",". $row['Value'] . "\n"; - } - - return new \LORIS\Http\Response\JsonResponse( - [ - 'statistics' => $reusable_response, - 'projects' => $projects, - 'csv' => $csv, - ] - ); - } - /** * An ETagCalculator provides the ability to calculate an ETag for * an incoming HTTP request. @@ -87,7 +75,7 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator */ public function ETag(ServerRequestInterface $request): string { - return md5(json_encode((string) $this->_handleGET($request)->getBody())); + return md5(json_encode((string) $this->handle($request)->getBody())); } /** diff --git a/test/integrationtests/LorisIntegrationTest.class.inc b/test/integrationtests/LorisIntegrationTest.class.inc index f8cb4ffcccb..58ab81b4756 100755 --- a/test/integrationtests/LorisIntegrationTest.class.inc +++ b/test/integrationtests/LorisIntegrationTest.class.inc @@ -171,13 +171,13 @@ abstract class LorisIntegrationTest extends TestCase protected function login($username, $password) { $this->safeGet($this->url); - + /* $this->webDriver->wait(120, 1000)->until( WebDriverExpectedCondition::presenceOfElementLocated( WebDriverBy::Name("username") ) ); - + */ $usernameEl = $this->safeFindElement( WebDriverBy::Name("username") ); From 7df2508ab7ad032e1e81bdfd2a24cb874bc8a4fd Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 20:37:44 -0500 Subject: [PATCH 12/18] Clipboard icon --- .../login/assets/summaryStatisticsPhone.js | 10 ++++---- modules/login/css/login.css | 14 ++++++++++- modules/login/jsx/summaryStatistics.js | 23 ++++++++++++------- .../login/php/summary_statistics.class.inc | 6 ++--- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/modules/login/assets/summaryStatisticsPhone.js b/modules/login/assets/summaryStatisticsPhone.js index 36d5c30cd09..207d8e710a1 100644 --- a/modules/login/assets/summaryStatisticsPhone.js +++ b/modules/login/assets/summaryStatisticsPhone.js @@ -1,4 +1,4 @@ -import * as React from "react" +import * as React from 'react'; const SvgComponent = (props) => ( ( id="image0_16_246" width={680} height={194} - /> + /> + /> -) -export default SvgComponent \ No newline at end of file +); +export default SvgComponent; diff --git a/modules/login/css/login.css b/modules/login/css/login.css index c3395a31caa..bace57e7811 100644 --- a/modules/login/css/login.css +++ b/modules/login/css/login.css @@ -21,7 +21,19 @@ left: 8%; top: 10%; width: 55%; - height: 60%; + height: 90%; +} + +.stats-copy { + justify-content: flex-end; + margin-bottom: 10px; + position: absolute; + bottom: 10px; + right: 10px; +} + +.stats-copy:hover { + opacity: 1; } @media screen and (max-width: 768px) { diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js index f61f91bad38..d710f316f44 100644 --- a/modules/login/jsx/summaryStatistics.js +++ b/modules/login/jsx/summaryStatistics.js @@ -50,14 +50,7 @@ class SummaryStatistics extends Component { emptyOption={false} />
- { - navigator.clipboard.writeText(this.state.data.csv); - }} - > - Data - - {' '}in LORIS: + Data in LORIS:
{/* Statistics */} {(this.state.data.statistics).map((statistic) => { @@ -79,6 +72,20 @@ class SummaryStatistics extends Component { ; } })} + {/* Copy to clipboard */} +
+ { + navigator.clipboard.writeText(this.state.data.csv); + }} + /> +
); diff --git a/modules/login/php/summary_statistics.class.inc b/modules/login/php/summary_statistics.class.inc index 894ee6e62ca..2028b8f7a03 100644 --- a/modules/login/php/summary_statistics.class.inc +++ b/modules/login/php/summary_statistics.class.inc @@ -43,14 +43,14 @@ class Summary_Statistics extends \NDB_Page implements ETagCalculator "SELECT DISTINCT Project FROM Login_Summary_Statistics", [] ); - + // csv version for exporting to clipboard $reusable_response = iterator_to_array($response); $csv = "Project,Statistic,Value\n"; foreach ($reusable_response as $row) { - $csv .= $row['Project'] .",". $row['Title'] .",". $row['Value'] . "\n"; + $csv .= $row['Project'].",".$row['Title'].",".$row['Value'] . "\n"; } - + return new \LORIS\Http\Response\JsonResponse( [ 'statistics' => $reusable_response, From a599a0eac51d110a74592449704889d760e51457 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 20:42:25 -0500 Subject: [PATCH 13/18] Update readme --- SQL/0000-00-00-schema.sql | 2 ++ modules/login/README.md | 2 ++ tools/update_login_summary_statistics.php | 15 +-------------- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql index 13da9e2f7fc..8cbd5de69c3 100644 --- a/SQL/0000-00-00-schema.sql +++ b/SQL/0000-00-00-schema.sql @@ -117,6 +117,8 @@ CREATE TABLE `users` ( CONSTRAINT `FK_users_2` FOREIGN KEY (`language_preference`) REFERENCES `language` (`language_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + INSERT INTO `users` (ID,UserID,Real_name,First_name,Last_name,Email,Privilege,PSCPI,DBAccess,Active,Pending_approval,PasswordChangeRequired) VALUES (1,'admin','Admin account','Admin','account','admin@example.com',0,'N','','Y','N',0); diff --git a/modules/login/README.md b/modules/login/README.md index 9956e4413c2..fff9a2c97ba 100644 --- a/modules/login/README.md +++ b/modules/login/README.md @@ -43,6 +43,8 @@ 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 diff --git a/tools/update_login_summary_statistics.php b/tools/update_login_summary_statistics.php index de68ac9e542..031f7f40572 100644 --- a/tools/update_login_summary_statistics.php +++ b/tools/update_login_summary_statistics.php @@ -21,6 +21,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Usage: php update_login_summary_statistics.php + * Can be added to a project's cron to update the table regularly. * * PHP Version 8 * @@ -52,11 +53,6 @@ [] ); -// TODO: delete. This would return a query object instead of -// queryIDs and it was really difficult to try to get it to run -// $provisioner = ( -// new \LORIS\dataquery\Provisioners\StudyQueries($lorisInstance, 'loginpage') -// ); $pinnedqueries = $DB->pselectWithIndexKey( "SELECT dq.QueryID, Query, dsq.Name FROM dataquery_queries dq @@ -82,12 +78,6 @@ $queryName = $pin['Name']; $queryToOrder[$queryName] = $order; $order++; - // TODO: delete. Commented out after changing from - // hardcoded queryID to pinned queries - // Get the query Name - // $query = new \LORIS\dataquery\Query($lorisInstance, $queryID); - // $query->loadQueryMetadata($user); - // $queryName = $query->getQueryName(); // Initialize the counts $data['All Projects'][$queryName] = -1; @@ -106,11 +96,8 @@ while ($response->eof() == false) { $next = $response->read(1024); if (!empty($next)) { - // TODO: add as a OBJECT not string somehow $result[] = $next; $inAProject = false; - // TODO: this processes as string by looking for the project name. - // Change to something better when it is an object. foreach ($projects as $project) { if (strpos($next, $project) !== false) { $data[$project][$queryName] += 1; From 066648ccd72266990d867dc5f9b23771f656b11c Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Wed, 22 Jan 2025 21:10:10 -0500 Subject: [PATCH 14/18] Remove leftover code --- modules/dataquery/php/query.class.inc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/modules/dataquery/php/query.class.inc b/modules/dataquery/php/query.class.inc index cdece610a50..02471d7e6ac 100644 --- a/modules/dataquery/php/query.class.inc +++ b/modules/dataquery/php/query.class.inc @@ -368,16 +368,6 @@ class Query implements \LORIS\StudyEntities\AccessibleResource, ); } - /** - * Get the name of the query. - * - * @return void - */ - public function getQueryName() - { - return $this->name; - } - /** * Get the Criteria for this query or null if there is no * criteria. From e5e230529fe1546284c04f9b6cc487458c3e1b9a Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Thu, 23 Jan 2025 10:30:22 -0500 Subject: [PATCH 15/18] Final Cleanup --- SQL/9999-99-99-drop_tables.sql | 1 + .../07_EEG Recording.sql | 12 +- .../login/assets/summaryStatisticsPhone.js | 105 +++++++++++++++--- modules/login/css/login.css | 5 +- modules/login/jsx/summaryStatistics.js | 4 +- raisinbread/RB_files/RB_Project.sql | 8 +- 6 files changed, 101 insertions(+), 34 deletions(-) diff --git a/SQL/9999-99-99-drop_tables.sql b/SQL/9999-99-99-drop_tables.sql index c40942b9815..e510d4386fe 100644 --- a/SQL/9999-99-99-drop_tables.sql +++ b/SQL/9999-99-99-drop_tables.sql @@ -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`; diff --git a/SQL/Login_Summary_Statistics/07_EEG Recording.sql b/SQL/Login_Summary_Statistics/07_EEG Recording.sql index 0afa65e4713..f1ed1ccf961 100644 --- a/SQL/Login_Summary_Statistics/07_EEG Recording.sql +++ b/SQL/Login_Summary_Statistics/07_EEG Recording.sql @@ -1,17 +1,17 @@ SELECT IFNULL(Project.Name, 'All Projects') as ProjectName, COUNT(PhysiologicalFileID) AS count -FROM physiological_parameter_file +FROM physiological_parameter_file ppf LEFT JOIN physiological_file USING (PhysiologicalFileID) LEFT JOIN physiological_output_type USING (PhysiologicalOutputTypeID) -LEFT JOIN Project ON physiological_parameter_file.ProjectID = Project.ProjectID +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 ProjectName WITH ROLLUP \ No newline at end of file +) +-- AND OutputTypeName = 'raw' +AND Project.showSummaryOnLogin = 1 +GROUP BY Project.Name WITH ROLLUP \ No newline at end of file diff --git a/modules/login/assets/summaryStatisticsPhone.js b/modules/login/assets/summaryStatisticsPhone.js index 207d8e710a1..15e0f73cb2e 100644 --- a/modules/login/assets/summaryStatisticsPhone.js +++ b/modules/login/assets/summaryStatisticsPhone.js @@ -10,71 +10,142 @@ const SvgComponent = (props) => ( > +
Date: Mon, 10 Feb 2025 10:46:53 -0500 Subject: [PATCH 16/18] Class component to functional component --- .../2024-12-18-Login_statistics.sql | 1 - modules/login/jsx/summaryStatistics.js | 123 +++++++----------- 2 files changed, 49 insertions(+), 75 deletions(-) diff --git a/SQL/New_patches/2024-12-18-Login_statistics.sql b/SQL/New_patches/2024-12-18-Login_statistics.sql index 06a307e3fd4..8d42bbaf498 100644 --- a/SQL/New_patches/2024-12-18-Login_statistics.sql +++ b/SQL/New_patches/2024-12-18-Login_statistics.sql @@ -1,4 +1,3 @@ -DROP TABLE IF EXISTS Login_Summary_Statistics; CREATE TABLE Login_Summary_Statistics ( Title VARCHAR(255), Project VARCHAR(255), diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js index 58b5340798f..e2b7c3a5dfc 100644 --- a/modules/login/jsx/summaryStatistics.js +++ b/modules/login/jsx/summaryStatistics.js @@ -1,65 +1,43 @@ -import React, {Component} from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import {SelectElement} from 'jsx/Form'; +import { SelectElement } from 'jsx/Form'; import SummaryStatisticsPhone from '../assets/summaryStatisticsPhone.js'; /** * Login Summary Statistics * - * * @author Saagar Arya * @version 1.0.0 */ -class SummaryStatistics extends Component { - /** - * @constructor - * @param {object} props - React Component properties - */ - constructor(props) { - super(props); - - this.state = { - selectedProject: props.data.projects.includes('All Projects') - ? props.data.projects.indexOf('All Projects') - : '0', - data: props.data, - }; - } +const SummaryStatistics = ({ data }) => { + const [selectedProject, setSelectedProject] = useState( + data.projects.includes('All Projects') + ? data.projects.indexOf('All Projects') + : '0' + ); - /** - * Renders the React component. - * - * @return {JSX} - React markup for the component - */ - render() { - return ( -
- - {/* Phone screen */} -
- {/* Project Selector */} - { - this.setState({ - selectedProject: value, - }); - }} - emptyOption={false} - /> -
- Data in LORIS: -
- {/* Statistics */} - {(this.state.data.statistics).map((statistic) => { - if ( - statistic.Project - == this.state.data.projects[this.state.selectedProject] - && statistic.Value > 0 - ) { - return
+ return ( +
+ + {/* Phone screen */} +
+ {/* Project Selector */} + setSelectedProject(value)} + emptyOption={false} + /> +
Data in LORIS:
+ {/* Statistics */} + {data.statistics.map((statistic, index) => { + if ( + statistic.Project === data.projects[selectedProject] && + statistic.Value > 0 + ) { + return ( +
{statistic.Value.toLocaleString('fr-CA')}{' '} - {statistic.Title}{statistic.Value != 1 ? 's' : ''} -
; - } - })} - {/* Copy to clipboard */} -
- { - navigator.clipboard.writeText(this.state.data.csv); - }} - /> -
+ {statistic.Title} + {statistic.Value !== 1 ? 's' : ''} +
+ ); + } + return null; + })} + {/* Copy to clipboard */} +
+ navigator.clipboard.writeText(data.csv)} + />
- ); - } -} +
+ ); +}; SummaryStatistics.propTypes = { - DataURL: PropTypes.string.isRequired, - data: PropTypes.object, + data: PropTypes.object.isRequired, }; -export default SummaryStatistics; +export default SummaryStatistics; \ No newline at end of file From 40a8e802f1f47e3566e9b5578098bf0de9153428 Mon Sep 17 00:00:00 2001 From: Saagar Arya Date: Mon, 10 Feb 2025 11:26:04 -0500 Subject: [PATCH 17/18] Cleanup --- modules/login/jsx/summaryStatistics.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/login/jsx/summaryStatistics.js b/modules/login/jsx/summaryStatistics.js index e2b7c3a5dfc..15bed5b3109 100644 --- a/modules/login/jsx/summaryStatistics.js +++ b/modules/login/jsx/summaryStatistics.js @@ -1,6 +1,6 @@ -import React, { useState } from 'react'; +import React, {useState} from 'react'; import PropTypes from 'prop-types'; -import { SelectElement } from 'jsx/Form'; +import {SelectElement} from 'jsx/Form'; import SummaryStatisticsPhone from '../assets/summaryStatisticsPhone.js'; /** @@ -9,7 +9,7 @@ import SummaryStatisticsPhone from '../assets/summaryStatisticsPhone.js'; * @author Saagar Arya * @version 1.0.0 */ -const SummaryStatistics = ({ data }) => { +const SummaryStatistics = ({data}) => { const [selectedProject, setSelectedProject] = useState( data.projects.includes('All Projects') ? data.projects.indexOf('All Projects') @@ -57,7 +57,7 @@ const SummaryStatistics = ({ data }) => {
navigator.clipboard.writeText(data.csv)} />
@@ -70,4 +70,4 @@ SummaryStatistics.propTypes = { data: PropTypes.object.isRequired, }; -export default SummaryStatistics; \ No newline at end of file +export default SummaryStatistics; From 69e4a96fb89dc1c84665af229185848c0c8b14a0 Mon Sep 17 00:00:00 2001 From: Saagar Arya <51128536+skarya22@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:52:53 -0500 Subject: [PATCH 18/18] Remove RB modification from release patch --- SQL/New_patches/2024-12-18-Login_statistics.sql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SQL/New_patches/2024-12-18-Login_statistics.sql b/SQL/New_patches/2024-12-18-Login_statistics.sql index 8d42bbaf498..34d8b6a4e31 100644 --- a/SQL/New_patches/2024-12-18-Login_statistics.sql +++ b/SQL/New_patches/2024-12-18-Login_statistics.sql @@ -10,6 +10,4 @@ ALTER TABLE dataquery_study_queries_rel MODIFY COLUMN PinType enum('topquery','dashboard', 'loginpage') DEFAULT NULL; ALTER TABLE Project -ADD COLUMN showSummaryOnLogin BOOLEAN DEFAULT TRUE; - -UPDATE Project SET showSummaryOnLogin = FALSE WHERE Name = 'DCP'; \ No newline at end of file +ADD COLUMN showSummaryOnLogin BOOLEAN DEFAULT TRUE; \ No newline at end of file