-
- {$candidate.DoB}
- |
- {if $candidate.EDC!=""}
+ {if $dob_age!=""}
+
+ {$dob_age}
+ |
+ {/if}
+ {if $edc_age!=""}
- {$candidate.EDC}
+ {$edc_age}
|
{/if}
diff --git a/test/test_instrument/test_instrumentTest.php b/test/test_instrument/test_instrumentTest.php
index a0a74332944..6f5475a69e2 100644
--- a/test/test_instrument/test_instrumentTest.php
+++ b/test/test_instrument/test_instrumentTest.php
@@ -35,7 +35,24 @@ public function setUp(): void
'Active' => 'Y',
'UserID' => 1,
'Entity_type' => 'Human',
- 'Sex' => 'Female'
+ 'ExternalID' => null,
+ 'DoB' => '2003-10-31',
+ 'DoD' => null,
+ 'EDC' => '2003-11-30',
+ 'Sex' => 'Male',
+ 'Ethnicity' => null,
+ 'Active' => 'Y',
+ 'Date_active' => '2016-01-23',
+ 'RegisteredBy' => null,
+ 'Date_registered' => '2016-01-23',
+ 'flagged_caveatemptor' => 'false',
+ 'flagged_reason' => null,
+ 'flagged_other' => null,
+ 'flagged_other_status' => null,
+ 'Testdate' => '2019-06-20 12:10:04',
+ 'Entity_type' => 'Human',
+ 'ProbandSex' => null,
+ 'ProbandDoB' => null
]
);
$this->DB->insert(
@@ -95,7 +112,7 @@ public function tearDown(): void
private function _testContent($content)
{
$this->_landing();
- $bodyText = $this->safeFindElement(WebDriverBy::cssSelector("#page"))
+ $bodyText = $this->safeFindElement(WebDriverBy::cssSelector("body"))
->getText();
$this->assertStringContainsString($content, $bodyText);
}
@@ -248,4 +265,3 @@ private function _landing()
);
}
}
-
From 1ea7737b1763fe68751eaf65bc4331ed338da7a0 Mon Sep 17 00:00:00 2001
From: Henri Rabalais
Date: Tue, 29 Oct 2024 19:03:56 +0100
Subject: [PATCH 10/28] [JSX] Relax Strictness of Filter for Multiselect in
Filter Component (#9402)
This reduces the strictness of the filter logic in the Filter.js
component by adjusting the conditions for the exactMatch variable,
thereby functionally changing the multiselect filter from an AND to an OR
boolean operators.
---
jsx/Filter.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jsx/Filter.js b/jsx/Filter.js
index db0edf90698..e0feb0cde8e 100644
--- a/jsx/Filter.js
+++ b/jsx/Filter.js
@@ -49,7 +49,7 @@ function Filter(props) {
const type = fields
.find((field) => (field.filter||{}).name == name).filter.type;
const exactMatch = (!(type === 'text' || type === 'date'
- || type === 'datetime'));
+ || type === 'datetime' || type === 'multiselect'));
if (value === null || value === '' ||
(value.constructor === Array && value.length === 0) ||
(type === 'checkbox' && value === false)) {
From 43108f9bac238169dae54cc09f1d0cbdcf99b7de Mon Sep 17 00:00:00 2001
From: Saagar Arya <51128536+skarya22@users.noreply.github.com>
Date: Tue, 29 Oct 2024 14:04:40 -0400
Subject: [PATCH 11/28] [Tools] CouchDB tools fix and PHP warning removal
(#9427)
- Updated CouchDB import tools to remove some PHP warnings
- Updated CouchDB instrument import tool to actually work after flag
---
tools/CouchDB_Import_Instruments.php | 7 ++++---
tools/importers/CouchDB_MRI_Importer.php | 4 ++--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/tools/CouchDB_Import_Instruments.php b/tools/CouchDB_Import_Instruments.php
index 4883ee3ab2c..cc7e5b2e5dc 100755
--- a/tools/CouchDB_Import_Instruments.php
+++ b/tools/CouchDB_Import_Instruments.php
@@ -142,10 +142,11 @@ function generateDocumentSQL(string $tablename) : string
$from = "FROM flag f
JOIN session s ON(s.ID=f.SessionID)
JOIN candidate c ON(c.CandID=s.CandID)
- LEFT JOIN flag ddef ON(ddef.CommentID=CONCAT('DDE_', f.CommentID))";
+ LEFT JOIN flag ddef ON(ddef.CommentID=CONCAT('DDE_', f.CommentID))
+ LEFT JOIN test_names tn ON(f.TestID = tn.ID)";
$where = "WHERE f.CommentID NOT LIKE 'DDE%'
- AND f.Test_name=:inst AND s.Active='Y' AND c.Active='Y'";
+ AND tn.Test_name=:inst AND s.Active='Y' AND c.Active='Y'";
if ($tablename === "") {
// the data is in the flag table, add the data column to the query
@@ -212,7 +213,7 @@ function updateCandidateDocs($Instruments)
if ($JSONData) {
//Transform JSON object into an array and add treat it the
//same as SQL
- $instrumentData = json_decode($row['Data'], true) ?? [];
+ $instrumentData = json_decode($row['Data'] ?? '', true) ?? [];
unset($row['Data']);
$docdata = $row + $instrumentData;
} else {
diff --git a/tools/importers/CouchDB_MRI_Importer.php b/tools/importers/CouchDB_MRI_Importer.php
index 4611b297599..10fb8151647 100644
--- a/tools/importers/CouchDB_MRI_Importer.php
+++ b/tools/importers/CouchDB_MRI_Importer.php
@@ -249,11 +249,11 @@ function _addMRIHeaderInfo($FileObj, $scan_type)
2
);
$header['SliceThickness_'.$type] = number_format(
- $FileObj->getParameter('slice_thickness'),
+ $FileObj->getParameter('slice_thickness') ?? 0,
2
);
$header['Time_'.$type] = number_format(
- $FileObj->getParameter('time'),
+ $FileObj->getParameter('time') ?? 0,
2
);
$header['Comment_'.$type] = $FileObj->getParameter('Comment');
From b08cb72091c954e161e5d6f93b6dce5d771555fe Mon Sep 17 00:00:00 2001
From: Shen
Date: Tue, 29 Oct 2024 14:09:18 -0400
Subject: [PATCH 12/28] [document_repository] Data framework (#8906)
Fixes #8894
---
.../php/docrepoprovisioner.class.inc | 81 ++++++++++++++++
.../php/docreporow.class.inc | 70 ++++++++++++++
.../php/document_repository.class.inc | 93 ++++++++++---------
3 files changed, 200 insertions(+), 44 deletions(-)
create mode 100644 modules/document_repository/php/docrepoprovisioner.class.inc
create mode 100644 modules/document_repository/php/docreporow.class.inc
diff --git a/modules/document_repository/php/docrepoprovisioner.class.inc b/modules/document_repository/php/docrepoprovisioner.class.inc
new file mode 100644
index 00000000000..8dcb6b9c864
--- /dev/null
+++ b/modules/document_repository/php/docrepoprovisioner.class.inc
@@ -0,0 +1,81 @@
+
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+
+namespace LORIS\document_repository;
+
+/**
+ * This class implements a data provisioner to get all modules
+ * for the module manager menu page.
+ *
+ * PHP Version 7
+ *
+ * @category Core
+ * @package Main
+ * @subpackage Core
+ * @author Shen Wang
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+class DocRepoProvisioner extends \LORIS\Data\Provisioners\DBRowProvisioner
+{
+ /**
+ * Create a ModuleManagerProvisioner, which gets modules for the
+ * module manager menu table.
+ */
+ function __construct()
+ {
+ parent::__construct(
+ "
+SELECT
+ v.File_name,
+ v.version,
+ v.File_type,
+ v.Instrument,
+ v.uploaded_by,
+ (SELECT name FROM psc WHERE CenterID = v.For_site) as site,
+ v.comments,
+ v.Date_uploaded,
+ v.record_id as Edit,
+ v.record_id as Delete_File,
+ v.File_category as category,
+ v.Data_dir,
+ v.For_site
+FROM
+ document_repository v
+WHERE
+ COALESCE(v.hide_video, false) = false
+ORDER BY
+ v.File_name;
+ ",
+ [],
+ );
+ }
+
+ /**
+ * Returns an instance of a HelpRow object for a given
+ * table row.
+ *
+ * @param array $row The database row from the LORIS Database class.
+ *
+ * @return \LORIS\Data\DataInstance An instance representing this row.
+ */
+ public function getInstance($row) : \LORIS\Data\DataInstance
+ {
+ $cid = \CenterID::singleton(intval($row['For_site']));
+ return new DocRepoRow($row, $cid);
+ }
+
+}
diff --git a/modules/document_repository/php/docreporow.class.inc b/modules/document_repository/php/docreporow.class.inc
new file mode 100644
index 00000000000..72af813003a
--- /dev/null
+++ b/modules/document_repository/php/docreporow.class.inc
@@ -0,0 +1,70 @@
+
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+
+namespace LORIS\document_repository;
+
+/**
+ * A ModuleRow represents a row in the acknowledgements menu table.
+ *
+ * The acknowledgements requires a specific "row" concept because
+ * the \Module class does not have any concept of the Active flag
+ * for the module in order to serialize it as JSON for the data
+ * table.
+ *
+ * @category Core
+ * @package Main
+ * @subpackage Core
+ * @author Dave MacFarlane
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+class DocRepoRow implements
+ \LORIS\Data\DataInstance,
+ \LORIS\StudyEntities\SiteHaver
+{
+ protected $DBRow;
+
+ private \CenterID $CenterID;
+ /**
+ * Create a new HelpRow
+ *
+ * @param array $row The row
+ * @param \CenterID $cid The CenterID
+ */
+ public function __construct(array $row, \CenterID $cid)
+ {
+ $this->DBRow = $row;
+ $this->CenterID = $cid;
+ }
+ /**
+ * Returns the CenterID for this row
+ *
+ * @return \CenterID
+ */
+ public function getCenterID(): \CenterID
+ {
+ return $this->CenterID;
+ }
+ /**
+ * Implements \LORIS\Data\DataInstance interface for this row.
+ *
+ * @return array which can be serialized by json_encode()
+ */
+ public function jsonSerialize() : array
+ {
+ return $this->DBRow;
+ }
+}
diff --git a/modules/document_repository/php/document_repository.class.inc b/modules/document_repository/php/document_repository.class.inc
index 0b5f73fd3ef..d6d9ad0b5fa 100644
--- a/modules/document_repository/php/document_repository.class.inc
+++ b/modules/document_repository/php/document_repository.class.inc
@@ -23,7 +23,7 @@ namespace LORIS\document_repository;
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
* @link https://www.github.com/aces/Loris
*/
-class Document_Repository extends \NDB_Menu_Filter_Form
+class Document_Repository extends \DataFrameworkMenu
{
private const PERMISSIONS = [
'document_repository_view',
@@ -46,13 +46,12 @@ class Document_Repository extends \NDB_Menu_Filter_Form
return $user->hasAnyPermission(self::PERMISSIONS);
}
/**
- * Function _setupVariables
+ * Function getFieldOptions
*
- * @return void
+ * @return array
*/
- function setup()
+ protected function getFieldOptions() : array
{
- parent::setup();
$user = \User::singleton();
$db = $this->loris->getDatabaseConnection();
$query = $db->pselect(
@@ -102,14 +101,13 @@ class Document_Repository extends \NDB_Menu_Filter_Form
foreach ($fileTypeQuery as $filetype) {
$fileTypeList[$filetype['File_type']] = $filetype['File_type'];
}
- $this->fieldOptions = [
+ return [
'fileCategories' => $categoriesList,
'sites' => $siteList,
'instruments' => $instrumentsList,
'fileTypes' => $fileTypeList,
'hiddenFile' => ['yes' => "Yes", 'no' => "No"],
];
- return;
}
/**
* Handler of parsing category
@@ -143,54 +141,61 @@ class Document_Repository extends \NDB_Menu_Filter_Form
"id" => $id,
];
}
+ /**
+ * Converts the results of this menu filter to a JSON format to be retrieved
+ * with ?format=json
+ *
+ * @return string a json encoded string of the headers and data from this table
+ */
+ public function toJSON(): string
+ {
+ $table = (new \LORIS\Data\Table())
+ ->withDataFrom($this->getDataProvisionerWithFilters());
+ $arr = array_map(
+ function ($row) {
+ return array_values($row);
+ },
+ json_decode($table->toJSON(\NDB_Factory::singleton()->user()), true)
+ );
+ return json_encode(
+ [
+ 'Data' => $arr,
+ 'fieldOptions' => $this->getFieldOptions(),
+ 'maxUploadSize' => \Utility::getMaxUploadSize(),
+ ]
+ );
+ }
/**
- * Build a list of media to display in Data Table
+ * {@inheritDoc}
*
- * @return void
- * @throws \DatabaseException
+ * @return bool
*/
- function _setupVariables()
+ public function useProjectFilter() : bool
{
- $user = \User::singleton();
- // the base query
- $query = " FROM document_repository v";
- $query .= " WHERE COALESCE(v.hide_video, false)=false";
- if (!$user->hasPermission('document_repository_hidden')) {
- $query .= " AND (hidden_file='no' OR hidden_file IS NULL)";
- }
- // set the class variables
- $this->columns = [
- 'v.File_name',
- 'v.version',
- 'v.File_type',
- 'v.Instrument',
- 'v.uploaded_by',
- '(SELECT name FROM psc WHERE CenterID=v.For_site) as site',
- 'v.comments',
- 'v.Date_uploaded',
- 'v.record_id as Edit',
- 'v.record_id as Delete_File',
- 'v.File_category as category',
- 'v.Data_dir',
- ];
- $this->query = $query;
- $this->group_by = '';
- $this->order_by = 'v.File_name';
+ return false;
}
-
/**
- * Converts the results of this menu filter to a JSON format to be retrieved
- * with ?format=json
+ * {@inheritDoc}
*
- * @return string a json encoded string of the headers and data from this table
+ * @return \Loris\Data\Provisioner
*/
- function toJSON(): string
+ public function getBaseDataProvisioner(): \LORIS\Data\Provisioner
{
- $result = $this->toArray();
- $result['maxUploadSize'] = \Utility::getMaxUploadSize();
- return json_encode($result);
+ return new DocRepoProvisioner();
}
+ /**
+ * Tells the base class that this page's provisioner can support the
+ * HasAnyPermissionOrUserSiteMatch filter.
+ *
+ * @return ?array of site permissions or null
+ */
+ public function allSitePermissionNames() : ?array
+ {
+ return ['access_all_profiles'];
+
+ }
+
/**
* Include additional CSS files:
From 7689455e89f5141841331335811690ffff163be1 Mon Sep 17 00:00:00 2001
From: Maxime Mulder
Date: Wed, 30 Oct 2024 10:38:00 -0400
Subject: [PATCH 13/28] Fix JS lint error on main (#9435)
#9339 got merged after #9430 without rebasing, which allowed some wrong
indents to be committed to main.
---
.../issue_tracker/jsx/issueTrackerIndex.js | 58 +++++++++----------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/modules/issue_tracker/jsx/issueTrackerIndex.js b/modules/issue_tracker/jsx/issueTrackerIndex.js
index fdf2ef0cc39..80baf03d9c1 100644
--- a/modules/issue_tracker/jsx/issueTrackerIndex.js
+++ b/modules/issue_tracker/jsx/issueTrackerIndex.js
@@ -277,36 +277,36 @@ class IssueTrackerIndex extends Component {
return (
-
-
+
+
+
+ {this.state.view === 'normal' ? (
+
+ ) : (
+
+ )}
- {this.state.view === 'normal' ? (
-
- ) : (
-
- )}
-
);
}
}
From a32d217badedab294dc85c4675cd18506d7832a1 Mon Sep 17 00:00:00 2001
From: Saagar Arya <51128536+skarya22@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:38:34 -0400
Subject: [PATCH 14/28] [Conflict_resolver] Show examiner name (#9404)
- Show the examiner name in the conflict resolver as well as the ID, as
just the ID makes it difficult for users.
- This was previously done in PR#2891 as well but the change was lost at
some point.
---
.../resolvedprovisioner.class.inc | 30 +++++++++++++++----
.../unresolvedprovisioner.class.inc | 14 +++++++--
2 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc b/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
index e5719d13341..51ca292cc04 100644
--- a/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
+++ b/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
@@ -39,12 +39,30 @@ class ResolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
candidate.PSCID as pscid,
session.Visit_label as visitlabel,
Project.Name as project,
- conflicts_resolved.FieldName as question,
- conflicts_resolved.OldValue1 as value1,
- conflicts_resolved.OldValue2 as value2,
- CASE
- WHEN conflicts_resolved.NewValue = 1
- THEN conflicts_resolved.OldValue1
+ conflicts_resolved.FieldName as question,
+ CASE
+ WHEN conflicts_resolved.FieldName <> "Examiner"
+ THEN conflicts_resolved.OldValue1
+ ELSE
+ CONCAT(conflicts_resolved.OldValue1, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_resolved.OldValue1))
+ END as value1,
+ CASE
+ WHEN conflicts_resolved.FieldName <> "Examiner"
+ THEN conflicts_resolved.OldValue2
+ ELSE CONCAT(conflicts_resolved.OldValue2, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_resolved.OldValue2))
+ END as value2,
+ CASE
+ WHEN conflicts_resolved.NewValue = 1
+ AND conflicts_resolved.FieldName <> "Examiner"
+ THEN conflicts_resolved.OldValue1
+ WHEN conflicts_resolved.NewValue = 1 AND conflicts_resolved.FieldName = "Examiner"
+ THEN CONCAT(conflicts_resolved.OldValue1, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_resolved.OldValue1))
+ WHEN conflicts_resolved.NewValue <> 1 AND conflicts_resolved.FieldName = "Examiner"
+ THEN CONCAT(conflicts_resolved.OldValue2, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_resolved.OldValue2))
ELSE conflicts_resolved.OldValue2
END AS correctanswer,
conflicts_resolved.ResolutionTimestamp as resolutiontimestamp,
diff --git a/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc b/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
index c5a3dd01d52..de3ab8395bb 100644
--- a/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
+++ b/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
@@ -40,8 +40,18 @@ class UnresolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
session.Visit_label as visitlabel,
Project.Name as project,
conflicts_unresolved.FieldName as question,
- conflicts_unresolved.Value1 as value1,
- conflicts_unresolved.Value2 as value2,
+ CASE
+ WHEN conflicts_unresolved.FieldName = "Examiner"
+ THEN CONCAT(conflicts_unresolved.Value1, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_unresolved.Value1))
+ ELSE conflicts_unresolved.Value1
+ END AS value1,
+ CASE
+ WHEN conflicts_unresolved.FieldName = "Examiner"
+ THEN CONCAT(conflicts_unresolved.Value2, " - ",
+ (SELECT full_name FROM examiners WHERE examinerID = conflicts_unresolved.Value2))
+ ELSE conflicts_unresolved.Value2
+ END AS value2,
psc.name as site,
session.CenterID as centerid,
Project.ProjectID as projectid
From 3c1baff1b670724c0be20ce6b03a905196a4f59f Mon Sep 17 00:00:00 2001
From: Victoria Hoang <118122474+victori444@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:57:31 -0400
Subject: [PATCH 15/28] [conflict_resolver] Add Cohort filter and field (#9407)
Adds a 'Cohort' filter and field to both unresolved and resolved tabs of
the conflict resolver
---
.../conflict_resolver/jsx/resolved_filterabledatatable.js | 5 +++++
.../conflict_resolver/jsx/unresolved_filterabledatatable.js | 5 +++++
modules/conflict_resolver/php/endpoints/resolved.class.inc | 2 ++
modules/conflict_resolver/php/endpoints/unresolved.class.inc | 2 ++
modules/conflict_resolver/php/models/resolveddto.class.inc | 2 ++
modules/conflict_resolver/php/models/unresolveddto.class.inc | 2 ++
.../php/provisioners/resolvedprovisioner.class.inc | 4 +++-
.../php/provisioners/unresolvedprovisioner.class.inc | 4 +++-
8 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/modules/conflict_resolver/jsx/resolved_filterabledatatable.js b/modules/conflict_resolver/jsx/resolved_filterabledatatable.js
index 8bdf41ca35d..01f4d9eb5a0 100644
--- a/modules/conflict_resolver/jsx/resolved_filterabledatatable.js
+++ b/modules/conflict_resolver/jsx/resolved_filterabledatatable.js
@@ -132,6 +132,11 @@ class ResolvedFilterableDataTable extends Component {
type: 'select',
options: options.project,
}},
+ {label: 'Cohort', show: true, filter: {
+ name: 'Cohort',
+ type: 'select',
+ options: options.cohort,
+ }},
{label: 'Site', show: true, filter: {
name: 'Site',
type: 'select',
diff --git a/modules/conflict_resolver/jsx/unresolved_filterabledatatable.js b/modules/conflict_resolver/jsx/unresolved_filterabledatatable.js
index 9ff1c14d4fc..16872dc571c 100644
--- a/modules/conflict_resolver/jsx/unresolved_filterabledatatable.js
+++ b/modules/conflict_resolver/jsx/unresolved_filterabledatatable.js
@@ -144,6 +144,11 @@ class UnresolvedFilterableDataTable extends Component {
type: 'select',
options: options.project,
}},
+ {label: 'Cohort', show: true, filter: {
+ name: 'cohort',
+ type: 'select',
+ options: options.cohort,
+ }},
{label: 'Site', show: true, filter: {
name: 'Site',
type: 'select',
diff --git a/modules/conflict_resolver/php/endpoints/resolved.class.inc b/modules/conflict_resolver/php/endpoints/resolved.class.inc
index ee00ca9a8e7..6b88e490b33 100644
--- a/modules/conflict_resolver/php/endpoints/resolved.class.inc
+++ b/modules/conflict_resolver/php/endpoints/resolved.class.inc
@@ -88,12 +88,14 @@ class Resolved extends Endpoint implements ETagCalculator
$sites = array_values(\Utility::getSiteList(false));
$visitlabels = array_values(\Utility::getVisitList());
$projects = array_values(\Utility::getProjectList());
+ $cohorts = array_values(\Utility::getCohortList());
return [
'site' => array_combine($sites, $sites),
'instrument' => \NDB_BVL_Instrument::getInstrumentNamesList($loris),
'visitLabel' => array_combine($visitlabels, $visitlabels),
'project' => array_combine($projects, $projects),
+ 'cohort' => array_combine($cohorts, $cohorts),
];
}
diff --git a/modules/conflict_resolver/php/endpoints/unresolved.class.inc b/modules/conflict_resolver/php/endpoints/unresolved.class.inc
index 74b6e39e85d..b9da6fa95c5 100644
--- a/modules/conflict_resolver/php/endpoints/unresolved.class.inc
+++ b/modules/conflict_resolver/php/endpoints/unresolved.class.inc
@@ -96,12 +96,14 @@ class Unresolved extends Endpoint implements ETagCalculator
$sites = array_values(\Utility::getSiteList(false));
$visitlabels = array_values(\Utility::getVisitList());
$projects = array_values(\Utility::getProjectList());
+ $cohorts = array_values(\Utility::getCohortList());
return [
'site' => array_combine($sites, $sites),
'instrument' => \NDB_BVL_Instrument::getInstrumentNamesList($loris),
'visitLabel' => array_combine($visitlabels, $visitlabels),
'project' => array_combine($projects, $projects),
+ 'cohort' => array_combine($cohorts, $cohorts),
];
}
diff --git a/modules/conflict_resolver/php/models/resolveddto.class.inc b/modules/conflict_resolver/php/models/resolveddto.class.inc
index 4dbdd50bf56..9451d3480a5 100644
--- a/modules/conflict_resolver/php/models/resolveddto.class.inc
+++ b/modules/conflict_resolver/php/models/resolveddto.class.inc
@@ -37,6 +37,7 @@ class ResolvedDTO implements DataInstance, SiteHaver
protected $resolvedid;
protected $projectid;
protected $project;
+ protected $cohort;
protected $centerid;
protected $site;
protected $candid;
@@ -63,6 +64,7 @@ class ResolvedDTO implements DataInstance, SiteHaver
return [
'ResolvedID' => $this->resolvedid,
'Project' => $this->project,
+ 'Cohort' => $this->cohort,
'Site' => $this->site,
'CandID' => $this->candid,
'PSCID' => $this->pscid,
diff --git a/modules/conflict_resolver/php/models/unresolveddto.class.inc b/modules/conflict_resolver/php/models/unresolveddto.class.inc
index 493a610fe9c..25a500ac67f 100644
--- a/modules/conflict_resolver/php/models/unresolveddto.class.inc
+++ b/modules/conflict_resolver/php/models/unresolveddto.class.inc
@@ -37,6 +37,7 @@ class UnresolvedDTO implements DataInstance, SiteHaver
protected $conflictid;
protected $projectid;
protected $project;
+ protected $cohort;
protected $centerid;
protected $site;
protected $candid;
@@ -58,6 +59,7 @@ class UnresolvedDTO implements DataInstance, SiteHaver
return [
'ConflictID' => $this->conflictid,
'Project' => $this->project,
+ 'Cohort' => $this->cohort,
'Site' => $this->site,
'CandID' => $this->candid,
'PSCID' => $this->pscid,
diff --git a/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc b/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
index 51ca292cc04..f087019b922 100644
--- a/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
+++ b/modules/conflict_resolver/php/provisioners/resolvedprovisioner.class.inc
@@ -71,7 +71,8 @@ class ResolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
conflicts_resolved.UserID as resolver,
psc.name as site,
session.CenterID as centerid,
- Project.ProjectID as projectid
+ Project.ProjectID as projectid,
+ cohort.title as cohort
FROM
conflicts_resolved
LEFT JOIN flag ON (conflicts_resolved.CommentId1=flag.CommentID)
@@ -79,6 +80,7 @@ class ResolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
LEFT JOIN candidate ON (candidate.CandID=session.CandID)
LEFT JOIN Project ON (session.ProjectID=Project.ProjectID )
LEFT JOIN psc ON (session.CenterID = psc.CenterID)
+ LEFT JOIN cohort ON (session.CohortID=cohort.CohortID)
WHERE session.Active="Y" AND candidate.Active ="Y"
',
[],
diff --git a/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc b/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
index de3ab8395bb..6f2cbce7cf3 100644
--- a/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
+++ b/modules/conflict_resolver/php/provisioners/unresolvedprovisioner.class.inc
@@ -54,7 +54,8 @@ class UnresolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
END AS value2,
psc.name as site,
session.CenterID as centerid,
- Project.ProjectID as projectid
+ Project.ProjectID as projectid,
+ cohort.title as cohort
FROM
conflicts_unresolved
LEFT JOIN flag ON (conflicts_unresolved.CommentId1=flag.CommentID)
@@ -62,6 +63,7 @@ class UnresolvedProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
LEFT JOIN candidate ON (candidate.CandID=session.CandID)
LEFT JOIN Project ON (session.ProjectID=Project.ProjectID)
LEFT JOIN psc ON (session.CenterID = psc.CenterID)
+ LEFT JOIN cohort ON (cohort.CohortID=session.CohortID)
WHERE session.Active="Y" AND candidate.Active ="Y"
',
[],
From b7596084077230db3858494531978438b139a898 Mon Sep 17 00:00:00 2001
From: Saagar Arya <51128536+skarya22@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:59:59 -0400
Subject: [PATCH 16/28] [NDB_BVL_Instrument] Fix for clearing instrument
(#9411)
Ignore commentID when clearing instrument.
When running clear instrument on a non JSON instrument the commentID
primary key is non-nullable, which it tries to set values to null which results in an
error, this is resolved.
Ignore CommentID when clearing.
---
php/libraries/NDB_BVL_Instrument.class.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/php/libraries/NDB_BVL_Instrument.class.inc b/php/libraries/NDB_BVL_Instrument.class.inc
index 65de2e8ebc3..ae173373848 100644
--- a/php/libraries/NDB_BVL_Instrument.class.inc
+++ b/php/libraries/NDB_BVL_Instrument.class.inc
@@ -2644,7 +2644,7 @@ abstract class NDB_BVL_Instrument extends NDB_Page
if (!$this->jsonData) {
$columns = $db->pselect(
"SELECT COLUMN_NAME FROM information_schema.columns
- WHERE TABLE_NAME=:table AND TABLE_SCHEMA=:db",
+ WHERE COLUMN_NAME<>'CommentID' AND TABLE_NAME=:table AND TABLE_SCHEMA=:db",
[
'table' => $this->table,
'db' => $dbconfig['database'],
From 7523ece67642c1fe6eabd5e1da96008b3f816931 Mon Sep 17 00:00:00 2001
From: Shen
Date: Wed, 30 Oct 2024 15:58:22 -0400
Subject: [PATCH 17/28] [Github] fix line format (#9438)
fix Line exceeds 85 characters; contains 87 characters
---
php/libraries/NDB_BVL_Instrument.class.inc | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/php/libraries/NDB_BVL_Instrument.class.inc b/php/libraries/NDB_BVL_Instrument.class.inc
index ae173373848..5babc30ebcf 100644
--- a/php/libraries/NDB_BVL_Instrument.class.inc
+++ b/php/libraries/NDB_BVL_Instrument.class.inc
@@ -2644,7 +2644,8 @@ abstract class NDB_BVL_Instrument extends NDB_Page
if (!$this->jsonData) {
$columns = $db->pselect(
"SELECT COLUMN_NAME FROM information_schema.columns
- WHERE COLUMN_NAME<>'CommentID' AND TABLE_NAME=:table AND TABLE_SCHEMA=:db",
+ WHERE COLUMN_NAME<>'CommentID' AND TABLE_NAME=:table
+ AND TABLE_SCHEMA=:db",
[
'table' => $this->table,
'db' => $dbconfig['database'],
From bf53274bb35de993ccf31ea70e0c6698b1376881 Mon Sep 17 00:00:00 2001
From: Saagar Arya <51128536+skarya22@users.noreply.github.com>
Date: Thu, 31 Oct 2024 10:43:10 -0400
Subject: [PATCH 18/28] [SQL][Instrument List][Battery Manager] Move DDE
Enabled to Test_Battery (#9264)
- Changed the logic for DDE being enabled to be moved from Config to Test_Battery so that it can be managed on a visit-instrument basis instead of just instrument.
- Modified Battery Manager to have a section for editing which battery has DDE enabled
---
SQL/0000-00-00-schema.sql | 1 +
SQL/0000-00-03-ConfigTables.sql | 1 -
.../2024_05_13_Add_DDE_To_Battery.sql | 9 ++
modules/api/docs/LorisRESTAPI_v0.0.4-dev.md | 2 +
.../endpoints/project/instruments.class.inc | 12 +-
.../php/endpoints/project/project.class.inc | 4 +-
.../models/projectinstrumentsrow.class.inc | 41 +++--
.../php/views/project/instruments.class.inc | 12 +-
.../battery_manager/jsx/batteryManagerForm.js | 8 +
.../jsx/batteryManagerIndex.js | 9 +-
modules/battery_manager/php/test.class.inc | 24 +--
.../php/testendpoint.class.inc | 4 +-
.../php/testoptionsendpoint.class.inc | 15 +-
.../php/testprovisioner.class.inc | 1 +
.../test/BatteryManagerTest.php | 10 +-
.../incompleteprovisioner.class.inc | 25 +--
.../php/instrument_list.class.inc | 18 ++-
.../instrument_list_controlpanel.class.inc | 41 +++--
php/libraries/NDB_BVL_Battery.class.inc | 81 +++++++---
php/libraries/NDB_BVL_Instrument.class.inc | 29 +++-
php/libraries/Utility.class.inc | 26 ++--
raisinbread/RB_files/RB_Config.sql | 5 -
raisinbread/RB_files/RB_ConfigSettings.sql | 1 -
raisinbread/RB_files/RB_test_battery.sql | 66 ++++----
.../instrument_sql/Meta/aosi_meta.sql | 6 +-
.../instrument_sql/Meta/bmi_meta.sql | 48 +++---
.../Meta/medical_history_meta.sql | 14 +-
.../Meta/mri_parameter_form_meta.sql | 72 ++++-----
.../Meta/radiology_review_meta.sql | 16 +-
raisinbread/test/api/LorisApiProjectsTest.php | 6 +
test/RBdata.sql | 2 +-
test/integrationtests/BatteryLookup_Test.php | 147 +++++++++---------
test/unittests/NDB_BVL_Instrument_Test.php | 11 +-
test/unittests/UtilityTest.php | 14 +-
.../delete_ignored_conflicts.php | 4 +-
tools/detect_conflicts.php | 4 +-
tools/detect_duplicated_commentids.php | 8 +-
tools/recreate_conflicts.php | 4 +-
38 files changed, 463 insertions(+), 338 deletions(-)
create mode 100644 SQL/New_patches/2024_05_13_Add_DDE_To_Battery.sql
diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql
index 887c2942f0e..1759cd8669f 100644
--- a/SQL/0000-00-00-schema.sql
+++ b/SQL/0000-00-00-schema.sql
@@ -341,6 +341,7 @@ CREATE TABLE `test_battery` (
`CenterID` int(11) default NULL,
`firstVisit` enum('Y','N') default NULL,
`instr_order` tinyint(4) default NULL,
+ `DoubleDataEntryEnabled` enum('Y','N') default 'N',
PRIMARY KEY (`ID`),
KEY `age_test` (`AgeMinDays`,`AgeMaxDays`,`Test_name`),
KEY `FK_test_battery_1` (`Test_name`),
diff --git a/SQL/0000-00-03-ConfigTables.sql b/SQL/0000-00-03-ConfigTables.sql
index f4fb22e499b..e7b4d637a0d 100644
--- a/SQL/0000-00-03-ConfigTables.sql
+++ b/SQL/0000-00-03-ConfigTables.sql
@@ -51,7 +51,6 @@ INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType,
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'useConsent', 'Enable if the study uses the loris architecture for consent', 1, 0, 'boolean', ID, 'Use consent', 16 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'additional_user_info', 'Display additional user profile fields on the User accounts page (e.g. Institution, Position, Country, Address)', 1, 0, 'boolean', ID, 'Additional user information', 17 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'excluded_instruments', "Instruments to be excluded from the Data Dictionary and download via the Data Query Tool", 1, 1, 'instrument', ID, 'Excluded instruments', 18 FROM ConfigSettings WHERE Name="study";
-INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'DoubleDataEntryInstruments', "Instruments for which double data entry should be enabled", 1, 1, 'instrument', ID, 'Double data entry instruments', 19 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'InstrumentResetting', 'Allows resetting of instrument data', 1, 0, 'boolean', ID, 'Instrument Resetting', 20 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'SupplementalSessionStatus', 'Display supplemental session status information on Timepoint List page', 1, 0, 'boolean', ID, 'Use Supplemental Session Status', 21 FROM ConfigSettings WHERE Name="study";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'useScanDone', 'Used for identifying timepoints that have (or should have) imaging data', 1, 0, 'boolean', ID, 'Use Scan Done', 22 FROM ConfigSettings WHERE Name="study";
diff --git a/SQL/New_patches/2024_05_13_Add_DDE_To_Battery.sql b/SQL/New_patches/2024_05_13_Add_DDE_To_Battery.sql
new file mode 100644
index 00000000000..5e508c7ef28
--- /dev/null
+++ b/SQL/New_patches/2024_05_13_Add_DDE_To_Battery.sql
@@ -0,0 +1,9 @@
+ALTER TABLE test_battery ADD COLUMN DoubleDataEntryEnabled enum("Y", "N") DEFAULT "N";
+
+UPDATE test_battery SET DoubleDataEntryEnabled = 'Y' WHERE Test_name IN (
+ SELECT Value from Config WHERE ConfigID = (SELECT ID FROM ConfigSettings WHERE Name = 'DoubleDataEntryInstruments')
+);
+
+DELETE FROM Config WHERE ConfigID IN (SELECT ID FROM ConfigSettings WHERE Name = 'DoubleDataEntryInstruments');
+
+DELETE FROM ConfigSettings WHERE Name = 'DoubleDataEntryInstruments';
diff --git a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md
index e445316e5b5..6edea3e19a0 100644
--- a/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md
+++ b/modules/api/docs/LorisRESTAPI_v0.0.4-dev.md
@@ -189,11 +189,13 @@ Will return a JSON object of the form:
"FullName" : "Long Name",
"Subgroup" : "Subgroup Name",
"DoubleDataEntryEnabled" : boolean
+ "DoubleDataEntryVisits" : array
},
"Instrument2" : {
"FullName" : "Long Name",
"Subgroup" : "Subgroup Name",
"DoubleDataEntryEnabled" : boolean
+ "DoubleDataEntryVisits" : array
},
...
}
diff --git a/modules/api/php/endpoints/project/instruments.class.inc b/modules/api/php/endpoints/project/instruments.class.inc
index 14eacd3be10..1b2d891b641 100644
--- a/modules/api/php/endpoints/project/instruments.class.inc
+++ b/modules/api/php/endpoints/project/instruments.class.inc
@@ -42,10 +42,13 @@ class Instruments extends Endpoint implements \LORIS\Middleware\ETagCalculator
/**
* Contructor
*
- * @param \Project $project The requested project
+ * @param \Project $project The requested project
+ * @param string $apiversion The version of the API being used
*/
- public function __construct(\Project $project)
- {
+ public function __construct(
+ \Project $project,
+ private string $apiversion = 'v0.0.3'
+ ) {
$this->_project = $project;
}
@@ -146,7 +149,8 @@ class Instruments extends Endpoint implements \LORIS\Middleware\ETagCalculator
$array = (new \LORIS\api\Views\Project\Instruments(
$this->_project,
- iterator_to_array($instruments)
+ iterator_to_array($instruments),
+ $this->apiversion
))->toArray();
$this->_cache = new \LORIS\Http\Response\JsonResponse($array);
diff --git a/modules/api/php/endpoints/project/project.class.inc b/modules/api/php/endpoints/project/project.class.inc
index c7baf1f10a8..105483a0ac7 100644
--- a/modules/api/php/endpoints/project/project.class.inc
+++ b/modules/api/php/endpoints/project/project.class.inc
@@ -100,6 +100,8 @@ class Project extends Endpoint implements \LORIS\Middleware\ETagCalculator
}
}
+ $apiversion = $request->getAttribute("LORIS-API-Version") ?? "unknown";
+
// Delegate to sub-endpoints
$subendpoint = array_shift($pathparts);
switch ($subendpoint) {
@@ -110,7 +112,7 @@ class Project extends Endpoint implements \LORIS\Middleware\ETagCalculator
$handler = new Images($this->_project);
break;
case 'instruments':
- $handler = new Instruments($this->_project);
+ $handler = new Instruments($this->_project, $apiversion);
break;
case 'visits':
$handler = new Visits($this->_project);
diff --git a/modules/api/php/models/projectinstrumentsrow.class.inc b/modules/api/php/models/projectinstrumentsrow.class.inc
index 58bd8634d51..76be7c809f7 100644
--- a/modules/api/php/models/projectinstrumentsrow.class.inc
+++ b/modules/api/php/models/projectinstrumentsrow.class.inc
@@ -27,25 +27,26 @@ class ProjectInstrumentsRow implements \LORIS\Data\DataInstance
private $_fullname;
private $_subgroupname;
private $_isDDE;
+ private $_ddeVisits;
/**
* Create a new ProjectImagesRow.
*
- * @param array $row An array of image properties
+ * @param array $row An array of image properties
+ * @param string $apiversion The version of the API being used
*/
- public function __construct(array $row)
- {
- $shortname = $row['shortname'] ?? null;
- $ddeinstruments = array_keys(\Utility::getAllDDEInstruments());
+ public function __construct(
+ array $row,
+ private string $apiversion = 'v0.0.3'
+ ) {
+ $this->_shortname = $row['shortname'] ?? null;
+ $this->_ddeVisits = \NDB_BVL_Battery::getDDEVisitsForInstrument(
+ $this->_shortname
+ );
- $this->_shortname = $shortname;
$this->_fullname = $row['fullname'] ?? null;
$this->_subgroupname = $row['subgroupname'] ?? null;
- $this->_isDDE = in_array(
- $shortname,
- $ddeinstruments,
- true
- );
+ $this->_isDDE = count($this->_ddeVisits) > 0;
}
/**
@@ -88,6 +89,16 @@ class ProjectInstrumentsRow implements \LORIS\Data\DataInstance
return $this->_isDDE;
}
+ /**
+ * Accessor for ddeVisits.
+ *
+ * @return array
+ */
+ public function getddeVisits(): array
+ {
+ return $this->_ddeVisits;
+ }
+
/**
* Implements \LORIS\Data\DataInstance interface for this row.
*
@@ -95,11 +106,17 @@ class ProjectInstrumentsRow implements \LORIS\Data\DataInstance
*/
public function jsonSerialize() : array
{
- return [
+ $obj = [
'shortname' => $this->_shortname,
'fullname' => $this->_fullname,
'subgroup' => $this->_subgroupname,
'ddeenable' => $this->_isDDE,
+ 'ddevisits' => $this->_ddeVisits,
];
+ if ($this->apiversion != 'v0.0.3') {
+ // api version >= v0.0.4
+ $obj['ddevisits'] = $this->_ddeVisits;
+ }
+ return $obj;
}
}
diff --git a/modules/api/php/views/project/instruments.class.inc b/modules/api/php/views/project/instruments.class.inc
index c117d463404..49a3ceb0c8f 100644
--- a/modules/api/php/views/project/instruments.class.inc
+++ b/modules/api/php/views/project/instruments.class.inc
@@ -45,9 +45,13 @@ class Instruments
*
* @param \Project $project The requested project
* @param ProjectInstrumentsRow[] $instruments An array of ProjectInstrumentsRow
+ * @param string $apiversion The version of the API being used
*/
- public function __construct(\Project $project, array $instruments)
- {
+ public function __construct(
+ \Project $project,
+ array $instruments,
+ private string $apiversion = 'v0.0.3'
+ ) {
$this->_project = $project;
$this->_instruments = $instruments;
}
@@ -70,6 +74,10 @@ class Instruments
'Subgroup' => $instrument->getSubgroupname(),
'DoubleDataEntryEnabled' => $instrument->isDDE(),
];
+ if ($this->apiversion != 'v0.0.3') {
+ // >= v0.0.4
+ $item['DoubleDataEntryVisits'] = $instrument->getddeVisits();
+ }
if (!is_null($shortname)) {
$instruments[$shortname] = $item;
diff --git a/modules/battery_manager/jsx/batteryManagerForm.js b/modules/battery_manager/jsx/batteryManagerForm.js
index 7277ab11b38..6f0329c78b5 100644
--- a/modules/battery_manager/jsx/batteryManagerForm.js
+++ b/modules/battery_manager/jsx/batteryManagerForm.js
@@ -145,6 +145,14 @@ class BatteryManagerForm extends Component {
max={127} // max value allowed by default column type of instr_order
value={test.instrumentOrder}
/>
+
diff --git a/modules/battery_manager/jsx/batteryManagerIndex.js b/modules/battery_manager/jsx/batteryManagerIndex.js
index c93e8030d07..40b3be79693 100644
--- a/modules/battery_manager/jsx/batteryManagerIndex.js
+++ b/modules/battery_manager/jsx/batteryManagerIndex.js
@@ -336,6 +336,11 @@ class BatteryManagerIndex extends Component {
name: 'instrumentOrder',
type: 'text',
}},
+ {label: 'Double Data Entry Enabled', show: true, filter: {
+ name: 'DoubleDataEntryEnabled',
+ type: 'select',
+ options: options.DoubleDataEntryEnabled,
+ }},
{label: 'Active', show: true, filter: {
name: 'active',
type: 'select',
@@ -365,6 +370,7 @@ class BatteryManagerIndex extends Component {
test.centerId,
test.firstVisit,
test.instrumentOrder,
+ test.DoubleDataEntryEnabled,
test.active,
];
});
@@ -420,7 +426,8 @@ class BatteryManagerIndex extends Component {
test.cohort == testCheck.cohort &&
test.visitLabel == testCheck.visitLabel &&
test.centerId == testCheck.centerId &&
- test.firstVisit == testCheck.firstVisit
+ test.firstVisit == testCheck.firstVisit &&
+ test.DoubleDataEntryEnabled == testCheck.DoubleDataEntryEnabled
) {
duplicate = testCheck;
}
diff --git a/modules/battery_manager/php/test.class.inc b/modules/battery_manager/php/test.class.inc
index 2c948025fca..f83fa27abb3 100644
--- a/modules/battery_manager/php/test.class.inc
+++ b/modules/battery_manager/php/test.class.inc
@@ -90,17 +90,19 @@ class Test implements
public function toSQL() : array
{
return [
- 'ID' => $this->row['id'] ?? null,
- 'Test_name' => $this->row['testName'] ?? null,
- 'AgeMinDays' => $this->row['ageMinDays'] ?? null,
- 'AgeMaxDays' => $this->row['ageMaxDays'] ?? null,
- 'Stage' => $this->row['stage'] ?? null,
- 'CohortID' => $this->row['cohort'] ?? null,
- 'Visit_label' => $this->row['visitLabel'] ?? null,
- 'CenterID' => $this->row['centerId'] ?? null,
- 'firstVisit' => $this->row['firstVisit'] ?? null,
- 'instr_order' => $this->row['instrumentOrder'] ?? null,
- 'Active' => $this->row['active'] ?? null,
+ 'ID' => $this->row['id'] ?? null,
+ 'Test_name' => $this->row['testName'] ?? null,
+ 'AgeMinDays' => $this->row['ageMinDays'] ?? null,
+ 'AgeMaxDays' => $this->row['ageMaxDays'] ?? null,
+ 'Stage' => $this->row['stage'] ?? null,
+ 'CohortID' => $this->row['cohort'] ?? null,
+ 'Visit_label' => $this->row['visitLabel'] ?? null,
+ 'CenterID' => $this->row['centerId'] ?? null,
+ 'firstVisit' => $this->row['firstVisit'] ?? null,
+ 'DoubleDataEntryEnabled' => $this->row['DoubleDataEntryEnabled']
+ ?? null,
+ 'instr_order' => $this->row['instrumentOrder'] ?? null,
+ 'Active' => $this->row['active'] ?? null,
];
}
}
diff --git a/modules/battery_manager/php/testendpoint.class.inc b/modules/battery_manager/php/testendpoint.class.inc
index 2e18205844a..b3c17c3b0d9 100644
--- a/modules/battery_manager/php/testendpoint.class.inc
+++ b/modules/battery_manager/php/testendpoint.class.inc
@@ -219,7 +219,8 @@ class TestEndpoint extends \NDB_Page implements RequestHandlerInterface
CohortID,
Visit_label,
CenterID,
- firstVisit
+ firstVisit,
+ DoubleDataEntryEnabled as DDE_enabled
FROM test_battery";
// Select duplicate entry from Test Battery
$entries = $this->db->pselect($query, []);
@@ -235,6 +236,7 @@ class TestEndpoint extends \NDB_Page implements RequestHandlerInterface
&& $testArray['Visit_label'] == $entry['Visit_label']
&& $testArray['CenterID'] == $entry['CenterID']
&& $testArray['firstVisit'] == $entry['firstVisit']
+ && $testArray['DoubleDataEntryEnabled'] == $entry['DDE_enabled']
) {
return true;
}
diff --git a/modules/battery_manager/php/testoptionsendpoint.class.inc b/modules/battery_manager/php/testoptionsendpoint.class.inc
index 77af508917c..6b2ba925349 100644
--- a/modules/battery_manager/php/testoptionsendpoint.class.inc
+++ b/modules/battery_manager/php/testoptionsendpoint.class.inc
@@ -64,15 +64,16 @@ class TestOptionsEndpoint extends \NDB_Page
$this->loris->getDatabaseConnection()
);
return [
- 'instruments' => \NDB_BVL_Instrument::getInstrumentNamesList(
+ 'instruments' => \NDB_BVL_Instrument::getInstrumentNamesList(
$this->loris
),
- 'stages' => $this->_getStageList(),
- 'cohorts' => \Utility::getCohortList(null),
- 'visits' => $visitController->getVisitlabels(),
- 'sites' => \Utility::getSiteList(false),
- 'firstVisit' => $this->_getYesNoList(),
- 'active' => $this->_getYesNoList(),
+ 'stages' => $this->_getStageList(),
+ 'cohorts' => \Utility::getCohortList(null),
+ 'visits' => $visitController->getVisitlabels(),
+ 'sites' => \Utility::getSiteList(false),
+ 'firstVisit' => $this->_getYesNoList(),
+ 'DoubleDataEntryEnabled' => $this->_getYesNoList(),
+ 'active' => $this->_getYesNoList(),
];
}
diff --git a/modules/battery_manager/php/testprovisioner.class.inc b/modules/battery_manager/php/testprovisioner.class.inc
index 777e52e347c..2ee3944850e 100644
--- a/modules/battery_manager/php/testprovisioner.class.inc
+++ b/modules/battery_manager/php/testprovisioner.class.inc
@@ -51,6 +51,7 @@ class TestProvisioner extends \LORIS\Data\Provisioners\DBRowProvisioner
b.Visit_label as visitLabel,
p.CenterID as centerId,
b.firstVisit,
+ b.DoubleDataEntryEnabled,
b.instr_order as instrumentOrder,
b.Active as active
FROM test_battery b
diff --git a/modules/battery_manager/test/BatteryManagerTest.php b/modules/battery_manager/test/BatteryManagerTest.php
index ce7e9327206..06c482854d7 100644
--- a/modules/battery_manager/test/BatteryManagerTest.php
+++ b/modules/battery_manager/test/BatteryManagerTest.php
@@ -138,7 +138,7 @@ function testLoadsWithPermissionEdit()
);
$this->safeClick(
WebDriverBy::cssSelector(
- "#dynamictable > tbody > tr:nth-child(1) > td:nth-child(13) > button"
+ "#dynamictable > tbody > tr:nth-child(1) > td:nth-child(14) > button"
)
);
$bodyText = $this->safeFindElement(
@@ -165,7 +165,7 @@ function testEditform()
$this->safeGet($this->url . "/battery_manager/");
$this->safeClick(
WebDriverBy::cssSelector(
- "#dynamictable > tbody > tr > td:nth-child(13) > button"
+ "#dynamictable > tbody > tr > td:nth-child(14) > button"
)
);
$this->safeClick(
@@ -209,7 +209,7 @@ function testEditform()
$this->safeClick(
WebDriverBy::cssSelector(
"#lorisworkspace>div>div:nth-child(2)>div>div:nth-child(2)>form>".
- " div > div:nth-child(11) > div > div > button"
+ " div > div:nth-child(12) > div > div > button"
)
);
$bodyText = $this->safeFindElement(
@@ -264,7 +264,7 @@ function testAddNew()
$this->safeClick(
WebDriverBy::cssSelector(
"#lorisworkspace > div >div:nth-child(2)>div>div:nth-child(2)>form ".
- "> div > div:nth-child(11) > div > div > button"
+ "> div > div:nth-child(12) > div > div > button"
)
);
$bodyText = $this->safeFindElement(
@@ -286,7 +286,7 @@ function testActivebtn()
$this->safeGet($this->url . "/battery_manager/");
$this->safeClick(
WebDriverBy::cssSelector(
- "#dynamictable > tbody > tr:nth-child(1) > td:nth-child(12) > button"
+ "#dynamictable > tbody > tr:nth-child(1) > td:nth-child(13) > button"
)
);
$bodyText = $this->safeFindElement(
diff --git a/modules/behavioural_qc/php/provisioners/incompleteprovisioner.class.inc b/modules/behavioural_qc/php/provisioners/incompleteprovisioner.class.inc
index 57ab07a50cd..f22f3096846 100644
--- a/modules/behavioural_qc/php/provisioners/incompleteprovisioner.class.inc
+++ b/modules/behavioural_qc/php/provisioners/incompleteprovisioner.class.inc
@@ -22,22 +22,6 @@ class IncompleteProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
*/
function __construct()
{
- $config =& \NDB_Config::singleton();
- $ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
- $db = \NDB_Factory::singleton()->database();
- for ($i=0; $iquote($ddeInstruments[$i]);
- }
- $where = "
- AND (f.commentid NOT LIKE 'DDE_%')
- ";
- if (count($ddeInstruments) > 0) {
- $ddeInstruments = implode(',', $ddeInstruments);
- $where = "
- AND (t.test_name IN ($ddeInstruments) OR
- f.commentid NOT LIKE 'DDE_%')
- ";
- }
parent::__construct(
"
SELECT DISTINCT
@@ -59,13 +43,20 @@ class IncompleteProvisioner extends \LORIS\Data\Provisioners\DBObjectProvisioner
JOIN candidate c ON (c.candid = s.candid)
JOIN test_names t ON (t.ID = f.TestID)
JOIN psc ON (s.CenterID = psc.CenterID)
+ JOIN test_battery ON (
+ t.Test_name = test_battery.Test_name
+ AND test_battery.Visit_label = s.Visit_label
+ )
WHERE
s.Active = 'Y'
AND c.Active = 'Y'
AND coalesce(f.data_entry, 'In Progress') = 'In Progress'
AND psc.Centerid != '1'
AND c.Entity_type != 'Scanner'
- $where
+ AND (
+ test_battery.DoubleDataEntryEnabled = 'Y'
+ OR f.commentid NOT LIKE 'DDE_%'
+ )
ORDER BY
f.commentid
",
diff --git a/modules/instrument_list/php/instrument_list.class.inc b/modules/instrument_list/php/instrument_list.class.inc
index ec27597d6d1..c613cdc95a5 100644
--- a/modules/instrument_list/php/instrument_list.class.inc
+++ b/modules/instrument_list/php/instrument_list.class.inc
@@ -223,9 +223,6 @@ class Instrument_List extends \NDB_Menu_Filter
$status = new \NDB_BVL_InstrumentStatus($this->loris);
$status->select($instrument['CommentID']);
- $ddeStatus = new \NDB_BVL_InstrumentStatus($this->loris);
- $ddeStatus->select($instrument['DDECommentID']);
-
$Ins = "instruments";
$ins = $instrument['Full_name'];
@@ -245,15 +242,22 @@ class Instrument_List extends \NDB_Menu_Filter
$this->tpl_data[$Ins][$x][$i]['commentID'] = $ins;
$ins = \NDB_BVL_Battery::isDoubleDataEntryEnabledForInstrument(
+ $this->timePoint->getVisitLabel(),
$instrument['Test_name']
);
+
$this->tpl_data[$Ins][$x][$i]['isDdeEnabled'] = $ins;
- $ins = $instrument['DDECommentID'];
- $this->tpl_data[$Ins][$x][$i]['ddeCommentID'] = $ins;
+ if ($ins) {
+ $ddeStatus = new \NDB_BVL_InstrumentStatus($this->loris);
+ $ddeStatus->select($instrument['DDECommentID']);
- $ins = $ddeStatus->getDataEntryStatus();
- $this->tpl_data[$Ins][$x][$i]['ddeDataEntryStatus'] = $ins;
+ $ins = $instrument['DDECommentID'];
+ $this->tpl_data[$Ins][$x][$i]['ddeCommentID'] = $ins;
+
+ $ins = $ddeStatus->getDataEntryStatus();
+ $this->tpl_data[$Ins][$x][$i]['ddeDataEntryStatus'] = $ins;
+ }
$ins = $instrument['isDirectEntry'];
$this->tpl_data[$Ins][$x][$i]['isDirectEntry'] = $ins;
diff --git a/modules/instrument_list/php/instrument_list_controlpanel.class.inc b/modules/instrument_list/php/instrument_list_controlpanel.class.inc
index 7f30bca6a36..0e50c271e89 100644
--- a/modules/instrument_list/php/instrument_list_controlpanel.class.inc
+++ b/modules/instrument_list/php/instrument_list_controlpanel.class.inc
@@ -133,8 +133,6 @@ class Instrument_List_ControlPanel extends \TimePoint
&& in_array($currentStage, ['Visit', 'Screening'])
&& $currentStatus != 'In Progress'
) {
- $config = \NDB_Config::singleton();
-
// Send to DCC - set Submitted to Y
$this->setData(
[
@@ -169,9 +167,11 @@ class Instrument_List_ControlPanel extends \TimePoint
$batteryList = $battery->getBatteryVerbose();
// Get the list of DDE instruments
- $config =& \NDB_Config::singleton();
- $ddeInstruments = $config->getSetting(
- 'DoubleDataEntryInstruments'
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList(
+ $this->loris,
+ $this->getData('Visit_label')
+ )
);
// clear the unresolved conflicts for all the instruments
@@ -235,9 +235,11 @@ class Instrument_List_ControlPanel extends \TimePoint
// get the list of instruments
$batteryList = $battery->getBatteryVerbose();
// Get the list of DDE instruments
- $config =& \NDB_Config::singleton();
- $ddeInstruments = $config->getSetting(
- 'DoubleDataEntryInstruments'
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList(
+ $this->loris,
+ $this->getData('Visit_label')
+ )
);
// detect conflicts for all the instruments
@@ -371,8 +373,12 @@ class Instrument_List_ControlPanel extends \TimePoint
$batteryList = $battery->getBatteryVerbose();
// Get the list of DDE instruments
- $config = \NDB_Config::singleton();
- $ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList(
+ $this->loris,
+ $this->getData('Visit_label')
+ )
+ );
// loop the list and check the Data Entry status
$ddeConflictDetected = false;
@@ -581,8 +587,12 @@ class Instrument_List_ControlPanel extends \TimePoint
$batteryList = $battery->getBatteryVerbose();
// Get the list of DDE instruments
- $config =& \NDB_Config::singleton();
- $ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList(
+ $this->loris,
+ $this->getData('Visit_label')
+ )
+ );
// clear the unresolved conflicts for all the instruments
$allDataEntryComplete =true;
@@ -678,7 +688,12 @@ class Instrument_List_ControlPanel extends \TimePoint
$batteryList = $battery->getBatteryVerbose();
// Get the list of DDE instruments
- $ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList(
+ $this->loris,
+ $this->getData('Visit_label')
+ )
+ );
// clear the unresolved conflicts for all the instruments
$allDataEntryComplete =true;
diff --git a/php/libraries/NDB_BVL_Battery.class.inc b/php/libraries/NDB_BVL_Battery.class.inc
index a0afb5fbec3..b6352446f2c 100644
--- a/php/libraries/NDB_BVL_Battery.class.inc
+++ b/php/libraries/NDB_BVL_Battery.class.inc
@@ -203,8 +203,9 @@ class NDB_BVL_Battery
throw new \LorisException("Invalid instrument");
}
- // get SessionID, UserID
- $query = "SELECT ID as SessionID, UserID FROM session WHERE ID=:SID";
+ // get SessionID, UserID, Visit_label
+ $query = "SELECT ID as SessionID, UserID, Visit_label
+ FROM session WHERE ID=:SID";
$rows = $DB->pselect($query, ['SID' => $this->sessionID]);
if (count($rows)==0) {
throw new Exception(
@@ -222,12 +223,18 @@ class NDB_BVL_Battery
// instantiate instrument object to get table name attribute
$obj = NDB_BVL_Instrument::factory($loris, $testName, '', '');
+ $DDEEnabled = $this->isDoubleDataEntryEnabledForInstrument(
+ $sessionData['Visit_label'],
+ $testName
+ );
if ($obj->usesJSONdata() !== true) {
// insert into the test table
$DB->insert($obj->table, ['CommentID' => $commentID]);
// insert the dde into the test table
- $DB->insert($obj->table, ['CommentID' => $ddeCommentID]);
+ if ($DDEEnabled) {
+ $DB->insert($obj->table, ['CommentID' => $ddeCommentID]);
+ }
}
$testId = $DB->pselectOne(
@@ -246,15 +253,17 @@ class NDB_BVL_Battery
);
// insert the dde into the flag table
- $DB->insert(
- 'flag',
- [
- 'SessionID' => $sessionData['SessionID'],
- 'TestID' => $testId,
- 'CommentID' => $ddeCommentID,
- 'UserID' => $sessionData['UserID'],
- ]
- );
+ if ($DDEEnabled) {
+ $DB->insert(
+ 'flag',
+ [
+ 'SessionID' => $sessionData['SessionID'],
+ 'TestID' => $testId,
+ 'CommentID' => $ddeCommentID,
+ 'UserID' => $sessionData['UserID'],
+ ]
+ );
+ }
return $commentID ?? '';
} // end addInstrument()
@@ -366,24 +375,52 @@ class NDB_BVL_Battery
return iterator_to_array($rows);
}
-
/**
* Checks if a test_name has double data entry enabled in the config
* for this Loris instance.
*
- * @param string $instrumentName The test_name that we want to know if
- * we're using DDE for.
+ * @param string $Visit_label The Visit_label that we want to
+ * check if DDE is enabled for.
+ * @param string $Test_name The test_name that we want to
+ * check if DDE is enabled for.
*
* @return boolean true if double data entry is enabled for this instrument
*/
- static function isDoubleDataEntryEnabledForInstrument($instrumentName)
- {
-
- $config =& NDB_Config::singleton();
- $doubleDataEntryInstruments
- = $config->getSetting("DoubleDataEntryInstruments");
+ static function isDoubleDataEntryEnabledForInstrument(
+ String $Visit_label,
+ String $Test_name
+ ) : bool {
+ $DB = \NDB_Factory::singleton()->database();
+ $ans = $DB->pselectOne(
+ 'SELECT DISTINCT DoubleDataEntryEnabled
+ FROM test_battery
+ WHERE Visit_label=:Visit_label
+ AND Test_name=:Test_name',
+ ['Visit_label' => $Visit_label, 'Test_name' => $Test_name]
+ ) == 'Y' ? true : false;
+
+ return $ans;
+ }
- return in_array($instrumentName, $doubleDataEntryInstruments);
+ /**
+ * Get the visits with DDE enabled for an instrument
+ *
+ * @param string $Test_name The test_name that we want to
+ * find DDE visits for.
+ *
+ * @return array with the Visit_labels
+ */
+ static function getDDEVisitsForInstrument(
+ String $Test_name
+ ) : array {
+ $DB = \NDB_Factory::singleton()->database();
+ return $DB->pselectCol(
+ "SELECT DISTINCT Visit_label
+ FROM test_battery
+ WHERE Test_name=:Test_name
+ AND DoubleDataEntryEnabled = 'Y'",
+ [':Test_name' => $Test_name]
+ );
}
/**
diff --git a/php/libraries/NDB_BVL_Instrument.class.inc b/php/libraries/NDB_BVL_Instrument.class.inc
index 5babc30ebcf..2c184bdac07 100644
--- a/php/libraries/NDB_BVL_Instrument.class.inc
+++ b/php/libraries/NDB_BVL_Instrument.class.inc
@@ -3347,20 +3347,35 @@ abstract class NDB_BVL_Instrument extends NDB_Page
* array with format $testName => $fullName. Only Names of instruments which are
* instantiable are returned, invalid instruments will not appear in this list.
*
- * @param \LORIS\LorisInstance $loris The LORIS instance to get the DDE from
+ * @param \LORIS\LorisInstance $loris The LORIS instance to get DDE from
+ * @param ?string $Visit_label The visit label to get the DDE for if
+ * null all DDE instruments are returned
*
* @return array
* @throws DatabaseException
*/
- static function getDDEInstrumentNamesList(\LORIS\LorisInstance $loris) : array
- {
- $config = $loris->getConfiguration();
+ static function getDDEInstrumentNamesList(
+ \LORIS\LorisInstance $loris,
+ String $Visit_label=null
+ ) : array {
+ $DB = $loris->getDatabaseConnection();
$instrumentNamesList = self::getInstrumentNamesList($loris);
- $doubleDataEntryInstruments = $config->getSetting(
- 'DoubleDataEntryInstruments'
- );
+ if (empty($Visit_label)) {
+ $doubleDataEntryInstruments = $DB->pselectCol(
+ "SELECT DISTINCT Test_name FROM test_battery
+ WHERE DoubleDataEntryEnabled='Y'",
+ []
+ );
+ } else {
+ $doubleDataEntryInstruments = $DB->pselectCol(
+ "SELECT DISTINCT Test_name FROM test_battery
+ WHERE DoubleDataEntryEnabled='Y'
+ AND Visit_label = :Visit_label",
+ ['Visit_label' => $Visit_label]
+ );
+ }
$instrumentNames = [];
foreach ($instrumentNamesList as $testName => $fullName) {
diff --git a/php/libraries/Utility.class.inc b/php/libraries/Utility.class.inc
index 108c733a8f4..7fa90d90bb2 100644
--- a/php/libraries/Utility.class.inc
+++ b/php/libraries/Utility.class.inc
@@ -580,24 +580,18 @@ class Utility
most accurate Full_name value and should be replaced by
getDDEInstrumentNamesList() of the NDB_BVL_Instrument class."
);
- $Factory = \NDB_Factory::singleton();
- $DB = $Factory->Database();
- $config = $Factory->config();
- $instruments_q = $DB->pselect(
- "SELECT Test_name,Full_name FROM test_names",
+ $Factory = \NDB_Factory::singleton();
+ $DB = $Factory->Database();
+ $ddeInstruments = $DB->pselect(
+ "SELECT DISTINCT test_battery.Test_name, Full_name
+ FROM test_battery
+ JOIN test_names ON test_battery.Test_name = test_names.Test_name
+ WHERE DoubleDataEntryEnabled = 'Y'",
[]
);
- $doubleDataEntryInstruments = $config->getSetting(
- 'DoubleDataEntryInstruments'
- );
-
- $instruments = [];
- foreach ($instruments_q as $row) {
- if (isset($row['Test_name']) && isset($row['Full_name'])) {
- if (in_array($row['Test_name'], $doubleDataEntryInstruments)) {
- $instruments[$row['Test_name']] = $row['Full_name'];
- }
- }
+ $instruments = [];
+ foreach ($ddeInstruments as $instrument) {
+ $instruments[$instrument['Test_name']] = $instrument['Full_name'];
}
return $instruments;
}
diff --git a/raisinbread/RB_files/RB_Config.sql b/raisinbread/RB_files/RB_Config.sql
index f7e70e6de08..7a91dd3186f 100644
--- a/raisinbread/RB_files/RB_Config.sql
+++ b/raisinbread/RB_files/RB_Config.sql
@@ -60,11 +60,6 @@ INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (65,83,'t1');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (66,84,'19');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (67,85,'/opt/niak-0.6.4.1/');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (68,86,'INTERLACE_outputDWIFileNameSuffix');
-INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (69,18,'aosi');
-INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (70,18,'bmi');
-INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (72,18,'medical_history');
-INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (73,18,'mri_parameter_form');
-INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (74,18,'radiology_review');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (75,87,'/issue_tracker');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (76,88,'localizer');
INSERT INTO `Config` (`ID`, `ConfigID`, `Value`) VALUES (77,88,'scout');
diff --git a/raisinbread/RB_files/RB_ConfigSettings.sql b/raisinbread/RB_files/RB_ConfigSettings.sql
index a9cc60a3cf1..6e5137f91f3 100644
--- a/raisinbread/RB_files/RB_ConfigSettings.sql
+++ b/raisinbread/RB_files/RB_ConfigSettings.sql
@@ -16,7 +16,6 @@ INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMult
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (14,'useProband','Enable for proband data collection',1,0,'boolean',1,'Use proband',11);
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (16,'useScreening','Enable if there is a Screening stage with its own distinct instruments, administered before the Visit stage',1,0,'boolean',1,'Use screening',14);
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (17,'excluded_instruments','Instruments to be excluded from the Data Dictionary and download via the Data Query Tool',1,1,'instrument',1,'Excluded instruments',16);
-INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (18,'DoubleDataEntryInstruments','Instruments for which double data entry should be enabled',1,1,'instrument',1,'Double data entry instruments',17);
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (19,'InstrumentResetting','Allows resetting of instrument data',1,0,'boolean',1,'Instrument Resetting',18);
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (20,'SupplementalSessionStatus','Display supplemental session status information on Timepoint List page',1,0,'boolean',1,'Use Supplemental Session Status',19);
INSERT INTO `ConfigSettings` (`ID`, `Name`, `Description`, `Visible`, `AllowMultiple`, `DataType`, `Parent`, `Label`, `OrderNumber`) VALUES (21,'useScanDone','Used for identifying timepoints that have (or should have) imaging data',1,0,'boolean',1,'Use Scan Done',20);
diff --git a/raisinbread/RB_files/RB_test_battery.sql b/raisinbread/RB_files/RB_test_battery.sql
index 8322d3653b0..7c2d6fba0b2 100644
--- a/raisinbread/RB_files/RB_test_battery.sql
+++ b/raisinbread/RB_files/RB_test_battery.sql
@@ -2,38 +2,38 @@ SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE `test_battery`;
LOCK TABLES `test_battery` WRITE;
-- AgeMaxDays maximum value is 99999
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (131,'aosi',1,43800,'Y','Visit',NULL,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (132,'aosi',1,4299,'Y','Visit',NULL,'V2',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (133,'aosi',4300,43800,'Y','Visit',NULL,'V3',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (134,'radiology_review',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (135,'radiology_review',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (136,'radiology_review',1,43800,'Y','Visit',1,'V2',2,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (137,'radiology_review',1,43800,'Y','Visit',2,'V2',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (138,'mri_parameter_form',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (139,'mri_parameter_form',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (140,'mri_parameter_form',1,43800,'Y','Visit',3,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (141,'mri_parameter_form',1,43800,'Y','Visit',4,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (142,'mri_parameter_form',1,43800,'Y','Visit',1,'V2',2,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (143,'mri_parameter_form',1,43800,'Y','Visit',2,'V2',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (144,'mri_parameter_form',1,43800,'Y','Visit',3,'V2',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (145,'mri_parameter_form',1,43800,'Y','Visit',4,'V2',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (146,'mri_parameter_form',1,43800,'Y','Visit',1,'V3',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (147,'mri_parameter_form',1,43800,'Y','Visit',2,'V3',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (148,'mri_parameter_form',1,43800,'Y','Visit',3,'V3',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (149,'mri_parameter_form',1,43800,'Y','Visit',4,'V3',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (150,'mri_parameter_form',1,43800,'Y','Visit',3,'V4',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (151,'mri_parameter_form',1,43800,'Y','Visit',4,'V4',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (152,'mri_parameter_form',1,43800,'Y','Visit',3,'V5',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (153,'mri_parameter_form',1,43800,'Y','Visit',4,'V5',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (154,'mri_parameter_form',1,43800,'Y','Visit',3,'V6',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (155,'mri_parameter_form',1,43800,'Y','Visit',4,'V6',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (156,'medical_history',1,43800,'Y','Visit',NULL,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (157,'medical_history',1,43800,'Y','Visit',NULL,'V2',NULL,'Y',NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (158,'medical_history',1,43800,'N','Visit',NULL,'V3',NULL,'N',NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (159,'medical_history',1,43800,'Y','Visit',NULL,'V4',NULL,'N',NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (160,'medical_history',1,43800,'Y','Visit',NULL,'V5',NULL,'N',NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (161,'medical_history',1,43800,'Y','Visit',NULL,'V6',NULL,'N',NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (162,'bmi',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL);
-INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`) VALUES (163,'bmi',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL);
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (131,'aosi',1,43800,'Y','Visit',NULL,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (132,'aosi',1,4299,'Y','Visit',NULL,'V2',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (133,'aosi',4300,43800,'Y','Visit',NULL,'V3',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (134,'radiology_review',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (135,'radiology_review',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (136,'radiology_review',1,43800,'Y','Visit',1,'V2',2,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (137,'radiology_review',1,43800,'Y','Visit',2,'V2',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (138,'mri_parameter_form',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (139,'mri_parameter_form',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (140,'mri_parameter_form',1,43800,'Y','Visit',3,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (141,'mri_parameter_form',1,43800,'Y','Visit',4,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (142,'mri_parameter_form',1,43800,'Y','Visit',1,'V2',2,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (143,'mri_parameter_form',1,43800,'Y','Visit',2,'V2',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (144,'mri_parameter_form',1,43800,'Y','Visit',3,'V2',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (145,'mri_parameter_form',1,43800,'Y','Visit',4,'V2',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (146,'mri_parameter_form',1,43800,'Y','Visit',1,'V3',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (147,'mri_parameter_form',1,43800,'Y','Visit',2,'V3',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (148,'mri_parameter_form',1,43800,'Y','Visit',3,'V3',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (149,'mri_parameter_form',1,43800,'Y','Visit',4,'V3',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (150,'mri_parameter_form',1,43800,'Y','Visit',3,'V4',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (151,'mri_parameter_form',1,43800,'Y','Visit',4,'V4',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (152,'mri_parameter_form',1,43800,'Y','Visit',3,'V5',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (153,'mri_parameter_form',1,43800,'Y','Visit',4,'V5',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (154,'mri_parameter_form',1,43800,'Y','Visit',3,'V6',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (155,'mri_parameter_form',1,43800,'Y','Visit',4,'V6',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (156,'medical_history',1,43800,'Y','Visit',NULL,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (157,'medical_history',1,43800,'Y','Visit',NULL,'V2',NULL,'Y',NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (158,'medical_history',1,43800,'N','Visit',NULL,'V3',NULL,'N',NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (159,'medical_history',1,43800,'Y','Visit',NULL,'V4',NULL,'N',NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (160,'medical_history',1,43800,'Y','Visit',NULL,'V5',NULL,'N',NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (161,'medical_history',1,43800,'Y','Visit',NULL,'V6',NULL,'N',NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (162,'bmi',1,43800,'Y','Visit',1,'V1',NULL,NULL,NULL, 'Y');
+INSERT INTO `test_battery` (`ID`, `Test_name`, `AgeMinDays`, `AgeMaxDays`, `Active`, `Stage`, `CohortID`, `Visit_label`, `CenterID`, `firstVisit`, `instr_order`, `DoubleDataEntryEnabled`) VALUES (163,'bmi',1,43800,'Y','Visit',2,'V1',NULL,NULL,NULL, 'Y');
UNLOCK TABLES;
SET FOREIGN_KEY_CHECKS=1;
diff --git a/raisinbread/instruments/instrument_sql/Meta/aosi_meta.sql b/raisinbread/instruments/instrument_sql/Meta/aosi_meta.sql
index c2998a118e1..7159f13adcc 100644
--- a/raisinbread/instruments/instrument_sql/Meta/aosi_meta.sql
+++ b/raisinbread/instruments/instrument_sql/Meta/aosi_meta.sql
@@ -4,7 +4,7 @@ INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_num
INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES('aosi', 'aosi_page2', 'General Observations', 2);
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V1');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V2');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V3');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V1', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V2', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('aosi', 1, 2147483647, 'Y', 'Visit', NULL, 'V3', 'Y');
diff --git a/raisinbread/instruments/instrument_sql/Meta/bmi_meta.sql b/raisinbread/instruments/instrument_sql/Meta/bmi_meta.sql
index 7076c3e20f2..2ab3660c2f6 100644
--- a/raisinbread/instruments/instrument_sql/Meta/bmi_meta.sql
+++ b/raisinbread/instruments/instrument_sql/Meta/bmi_meta.sql
@@ -1,27 +1,27 @@
INSERT INTO test_names (Test_name, Full_name, Sub_group) SELECT 'bmi', 'BMI Calculator', ID FROM test_subgroups WHERE Subgroup_name = 'Instruments';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'bmi', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6', 'Y' FROM cohort WHERE title = 'Stale';
diff --git a/raisinbread/instruments/instrument_sql/Meta/medical_history_meta.sql b/raisinbread/instruments/instrument_sql/Meta/medical_history_meta.sql
index 9da322a09b4..112460310dd 100644
--- a/raisinbread/instruments/instrument_sql/Meta/medical_history_meta.sql
+++ b/raisinbread/instruments/instrument_sql/Meta/medical_history_meta.sql
@@ -1,12 +1,12 @@
INSERT INTO test_names (Test_name, Full_name, Sub_group) SELECT 'medical_history', 'Medical History', ID FROM test_subgroups WHERE Subgroup_name = 'Instruments';
-INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES('medical_history', 'medical_history_page1', 'Page 1', 1);
+INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES('medical_history', 'medical_history_page1', 'Page 1', 'Y');
INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES('medical_history', 'medical_history_page2', 'Page 2', 2);
INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES('medical_history', 'medical_history_page3', 'Page 3', 3);
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V1');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V2');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V3');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V4');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V5');
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V6');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V1', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V2', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V3', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V4', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V5', 'Y');
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled) VALUES('medical_history', 1, 2147483647, 'Y', 'Visit', NULL, 'V6', 'Y');
diff --git a/raisinbread/instruments/instrument_sql/Meta/mri_parameter_form_meta.sql b/raisinbread/instruments/instrument_sql/Meta/mri_parameter_form_meta.sql
index b78fcb170cf..624d83885b6 100644
--- a/raisinbread/instruments/instrument_sql/Meta/mri_parameter_form_meta.sql
+++ b/raisinbread/instruments/instrument_sql/Meta/mri_parameter_form_meta.sql
@@ -2,39 +2,39 @@ INSERT INTO test_names (Test_name, Full_name, Sub_group) SELECT 'mri_parameter_f
INSERT INTO instrument_subtests (Test_name, Subtest_name, Description, Order_number) VALUES ('mri_parameter_form', 'mri_parameter_form_page1', 'Page 1', 1);
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'High Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'High Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3' FROM cohort WHERE title = 'High Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4' FROM cohort WHERE title = 'High Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5' FROM cohort WHERE title = 'High Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6' FROM cohort WHERE title = 'Low Yeast';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V3', 'Y' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V4', 'Y' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V5', 'Y' FROM cohort WHERE title = 'High Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6', 'Y' FROM cohort WHERE title = 'Low Yeast';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'mri_parameter_form', 1, 2147483647, 'Y', 'Visit', CohortID, 'V6', 'Y' FROM cohort WHERE title = 'High Yeast';
diff --git a/raisinbread/instruments/instrument_sql/Meta/radiology_review_meta.sql b/raisinbread/instruments/instrument_sql/Meta/radiology_review_meta.sql
index 3a84f19e023..b9bc0c214e2 100644
--- a/raisinbread/instruments/instrument_sql/Meta/radiology_review_meta.sql
+++ b/raisinbread/instruments/instrument_sql/Meta/radiology_review_meta.sql
@@ -1,10 +1,10 @@
INSERT INTO test_names (Test_name, Full_name, Sub_group) SELECT 'radiology_review', 'Radiology review', ID FROM test_subgroups WHERE Subgroup_name = 'Imaging';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1' FROM cohort WHERE title = 'Fresh';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Stale';
-INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label)
- SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V1', 'Y' FROM cohort WHERE title = 'Fresh';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Stale';
+INSERT INTO test_battery (Test_name, AgeMinDays, AgeMaxDays, Active, Stage, CohortID, Visit_label, DoubleDataEntryEnabled)
+ SELECT 'radiology_review', 1, 2147483647, 'Y', 'Visit', CohortID, 'V2', 'Y' FROM cohort WHERE title = 'Fresh';
diff --git a/raisinbread/test/api/LorisApiProjectsTest.php b/raisinbread/test/api/LorisApiProjectsTest.php
index e3481ae83a2..ee3a0f4ef3a 100644
--- a/raisinbread/test/api/LorisApiProjectsTest.php
+++ b/raisinbread/test/api/LorisApiProjectsTest.php
@@ -475,6 +475,12 @@ public function testGetProjectsProjectInstruments(): void
),
'boolean'
);
+ $this->assertSame(
+ gettype(
+ $projectsInstrArray['Instruments']['aosi']['DoubleDataEntryVisits']
+ ),
+ 'array'
+ );
$this->assertArrayHasKey(
'Meta',
diff --git a/test/RBdata.sql b/test/RBdata.sql
index 077498b061b..2fb19139edb 100644
--- a/test/RBdata.sql
+++ b/test/RBdata.sql
@@ -1442,7 +1442,7 @@ UNLOCK TABLES;
LOCK TABLES `test_battery` WRITE;
DELETE FROM `test_battery`;
/*!40000 ALTER TABLE `test_battery` DISABLE KEYS */;
-INSERT INTO `test_battery` (ID,Test_name,AgeMinDays,AgeMaxDays,Active,Stage,CohortID,Visit_label,CenterID,firstVisit,instr_order) VALUES (1,'radiology_review',0,99999,'Y','Visit',1,'V1',1,NULL,NULL),(2,'radiology_review',0,99999,'Y','Visit',2,'V1',2,NULL,NULL),(3,'bmi',1,99999,'Y','Visit',1,'V1',NULL,NULL,1),(4,'bmi',1,99999,'Y','Visit',2,'V1',NULL,NULL,1),(5,'tsi',150,210,'Y','Visit',1,'',NULL,'Y',NULL),(6,'tsi',300,420,'Y','Visit',2,'',NULL,'Y',NULL),(7,'tsi',150,420,'Y','Visit',3,'',NULL,'Y',NULL),(8,'aosi',1,1000,'Y','Visit',NULL,NULL,NULL,NULL,NULL),(9,'HandPreference',420,99999,'Y','Visit',NULL,'',NULL,NULL,NULL);
+INSERT INTO `test_battery` (ID,Test_name,AgeMinDays,AgeMaxDays,Active,Stage,CohortID,Visit_label,CenterID,firstVisit,instr_order,DoubleDataEntryEnabled) VALUES (1,'radiology_review',0,99999,'Y','Visit',1,'V1',1,NULL,NULL,'Y'),(2,'radiology_review',0,99999,'Y','Visit',2,'V1',2,NULL,NULL,'Y'),(3,'bmi',1,99999,'Y','Visit',1,'V1',NULL,NULL,1,'Y'),(4,'bmi',1,99999,'Y','Visit',2,'V1',NULL,NULL,1,'Y'),(5,'tsi',150,210,'Y','Visit',1,'',NULL,'Y',NULL,'Y'),(6,'tsi',300,420,'Y','Visit',2,'',NULL,'Y',NULL,'Y'),(7,'tsi',150,420,'Y','Visit',3,'',NULL,'Y',NULL,'Y'),(8,'aosi',1,1000,'Y','Visit',NULL,NULL,NULL,NULL,NULL,'Y'),(9,'HandPreference',420,99999,'Y','Visit',NULL,'',NULL,NULL,NULL,'Y');
/*!40000 ALTER TABLE `test_battery` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/test/integrationtests/BatteryLookup_Test.php b/test/integrationtests/BatteryLookup_Test.php
index ab0262a1443..7e1af3bf21a 100644
--- a/test/integrationtests/BatteryLookup_Test.php
+++ b/test/integrationtests/BatteryLookup_Test.php
@@ -99,88 +99,95 @@ function setUp(): void
"test_battery",
[
[
- 'ID' => 1,
- 'Test_name' => 'ActiveTestByAge',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 100,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 1,
- 'Visit_label' => null,
- 'CenterID' => null,
- 'firstVisit' => null,
+ 'ID' => 1,
+ 'Test_name' => 'ActiveTestByAge',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 100,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 1,
+ 'Visit_label' => null,
+ 'CenterID' => null,
+ 'firstVisit' => null,
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 2,
- 'Test_name' => 'ActiveTestByAge2',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 100,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 1,
- 'Visit_label' => null,
- 'CenterID' => '1',
- 'firstVisit' => null,
+ 'ID' => 2,
+ 'Test_name' => 'ActiveTestByAge2',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 100,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 1,
+ 'Visit_label' => null,
+ 'CenterID' => '1',
+ 'firstVisit' => null,
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 3,
- 'Test_name' => 'InactiveTest',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 0,
- 'Active' => 'N',
- 'Stage' => 'Visit',
- 'CohortID' => 2,
- 'Visit_label' => 'V01',
- 'CenterID' => '1',
- 'firstVisit' => null,
+ 'ID' => 3,
+ 'Test_name' => 'InactiveTest',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 0,
+ 'Active' => 'N',
+ 'Stage' => 'Visit',
+ 'CohortID' => 2,
+ 'Visit_label' => 'V01',
+ 'CenterID' => '1',
+ 'firstVisit' => null,
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 4,
- 'Test_name' => 'ActiveTestByVisit',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 0,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 2,
- 'Visit_label' => 'V01',
- 'CenterID' => null,
- 'firstVisit' => null,
+ 'ID' => 4,
+ 'Test_name' => 'ActiveTestByVisit',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 0,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 2,
+ 'Visit_label' => 'V01',
+ 'CenterID' => null,
+ 'firstVisit' => null,
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 5,
- 'Test_name' => 'ActiveTestByVisit2',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 0,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 2,
- 'Visit_label' => 'V01',
- 'CenterID' => '1',
- 'firstVisit' => null,
+ 'ID' => 5,
+ 'Test_name' => 'ActiveTestByVisit2',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 0,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 2,
+ 'Visit_label' => 'V01',
+ 'CenterID' => '1',
+ 'firstVisit' => null,
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 6,
- 'Test_name' => 'ActiveTestByFirstVisit',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 100,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 1,
- 'Visit_label' => null,
- 'CenterID' => '1',
- 'firstVisit' => 'Y',
+ 'ID' => 6,
+ 'Test_name' => 'ActiveTestByFirstVisit',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 100,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 1,
+ 'Visit_label' => null,
+ 'CenterID' => '1',
+ 'firstVisit' => 'Y',
+ 'DoubleDataEntryEnabled' => 'Y'
],
[
- 'ID' => 7,
- 'Test_name' => 'ActiveTestByNotFirstVisit',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 100,
- 'Active' => 'Y',
- 'Stage' => 'Visit',
- 'CohortID' => 1,
- 'Visit_label' => null,
- 'CenterID' => '1',
- 'firstVisit' => 'N',
+ 'ID' => 7,
+ 'Test_name' => 'ActiveTestByNotFirstVisit',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 100,
+ 'Active' => 'Y',
+ 'Stage' => 'Visit',
+ 'CohortID' => 1,
+ 'Visit_label' => null,
+ 'CenterID' => '1',
+ 'firstVisit' => 'N',
+ 'DoubleDataEntryEnabled' => 'Y'
],
]
);
diff --git a/test/unittests/NDB_BVL_Instrument_Test.php b/test/unittests/NDB_BVL_Instrument_Test.php
index 3da0e0c8ad7..905971d08fe 100644
--- a/test/unittests/NDB_BVL_Instrument_Test.php
+++ b/test/unittests/NDB_BVL_Instrument_Test.php
@@ -1957,11 +1957,12 @@ private function _setTableData()
"test_battery",
[
[
- 'Active' => 'Y',
- 'Test_name' => 'TestName1_proband',
- 'CohortID' => '12',
- 'AgeMinDays' => 0,
- 'AgeMaxDays' => 100
+ 'Active' => 'Y',
+ 'Test_name' => 'TestName1_proband',
+ 'CohortID' => '12',
+ 'AgeMinDays' => 0,
+ 'AgeMaxDays' => 100,
+ 'DoubleDataEntryEnabled' => 'Y'
]
]
);
diff --git a/test/unittests/UtilityTest.php b/test/unittests/UtilityTest.php
index 446e0903dd3..a38a61348c6 100644
--- a/test/unittests/UtilityTest.php
+++ b/test/unittests/UtilityTest.php
@@ -414,22 +414,16 @@ public function testGetInstruments()
*/
public function testGetAllDDEInstruments()
{
- $test_names = [
- ['Test_name' => 'test_name1',
- 'Full_name' => 'full_name1'
- ],
- ['Test_name' => 'test_name2',
+ $test_battery = [
+ [
+ 'Test_name' => 'test_name2',
'Full_name' => 'full_name2'
]
];
- $doubleDataEntryInstruments = ['test_name2'];
$this->_dbMock->expects($this->any())
->method('pselect')
- ->willReturn($test_names);
- $this->_configMock->expects($this->any())
- ->method('getSetting')
- ->willReturn($doubleDataEntryInstruments);
+ ->willReturn($test_battery);
$this->assertEquals(
['test_name2' => 'full_name2'],
diff --git a/tools/data_integrity/data_deletion/delete_ignored_conflicts.php b/tools/data_integrity/data_deletion/delete_ignored_conflicts.php
index d521af436eb..ebc6b007c09 100755
--- a/tools/data_integrity/data_deletion/delete_ignored_conflicts.php
+++ b/tools/data_integrity/data_deletion/delete_ignored_conflicts.php
@@ -68,7 +68,9 @@
$instruments[0] = $argv[1];
$instrumentSpecified = true;
} else {
- $instruments = $config->getSetting('DoubleDataEntryInstruments');
+ $instruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList($loris)
+ );
}
if (isset($instruments)) {
diff --git a/tools/detect_conflicts.php b/tools/detect_conflicts.php
index f3b6954b7c1..0e6e9142579 100755
--- a/tools/detect_conflicts.php
+++ b/tools/detect_conflicts.php
@@ -121,7 +121,9 @@
/// Initialization
$config = NDB_Config::singleton();
$db = $lorisInstance->getDatabaseConnection();
-$ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
+$ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList($lorisInstance)
+);
$db_config = $config->getSetting('database');
$paths = $config->getSetting('paths');
$dataDir = $paths['base'] . $config->getSetting('log');
diff --git a/tools/detect_duplicated_commentids.php b/tools/detect_duplicated_commentids.php
index d3da47da503..dbc4684548c 100755
--- a/tools/detect_duplicated_commentids.php
+++ b/tools/detect_duplicated_commentids.php
@@ -75,11 +75,9 @@
/**
* Initialization
*/
-$config = NDB_Config::singleton();
-$ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
-$dataDir = "logs";
-$diff = null;
-$commentids = [];
+$dataDir = "logs";
+$diff = null;
+$commentids = [];
//Check to see if the variable instrument is set
if (($instrument=='all') ||($instrument=='All')) {
$instruments = Utility::getAllInstruments();
diff --git a/tools/recreate_conflicts.php b/tools/recreate_conflicts.php
index ba66aafbb7e..48818f205e0 100755
--- a/tools/recreate_conflicts.php
+++ b/tools/recreate_conflicts.php
@@ -48,7 +48,9 @@
if ($action=='all') {
$allInstruments = NDB_BVL_Instrument::getInstrumentNamesList($lorisInstance);
- $ddeInstruments = $config->getSetting('DoubleDataEntryInstruments');
+ $ddeInstruments = array_keys(
+ \NDB_BVL_Instrument::getDDEInstrumentNamesList($lorisInstance)
+ );
} else {
$allInstruments = [$action => $action];
$ddeInstruments = [$action => $action];
From 38266d4ac7a63d320e00358f8c099a4da6c23311 Mon Sep 17 00:00:00 2001
From: Saagar Arya <51128536+skarya22@users.noreply.github.com>
Date: Thu, 31 Oct 2024 14:16:04 -0400
Subject: [PATCH 19/28] [data_release] Introduce Project Separation (#9385)
Add project separation permissions for the data release module
- This makes it so that users do not see data_release files from
different projects.
- If a data_release file does not have a projectID, then all users with
access to that version/file can see it.
- Improved the manage permission form because it was not very user
friendly
- Version and user options only show up as the ones that the current
user has access to that project for
---
SQL/0000-00-00-schema.sql | 4 +-
...-10-02-Data_Release_Project_separation.sql | 4 +
modules/data_release/jsx/addPermissionForm.js | 6 +-
modules/data_release/jsx/dataReleaseIndex.js | 6 +
.../data_release/jsx/managePermissionsForm.js | 107 +++++++++++++-----
modules/data_release/jsx/uploadFileForm.js | 17 +++
.../data_release/php/data_release.class.inc | 44 +++++--
.../php/datareleaseprovisioner.class.inc | 10 +-
modules/data_release/php/files.class.inc | 32 ++++--
raisinbread/RB_files/RB_data_release.sql | 2 +-
10 files changed, 177 insertions(+), 55 deletions(-)
create mode 100644 SQL/New_patches/2024-10-02-Data_Release_Project_separation.sql
diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql
index 1759cd8669f..d6c8a1ddce0 100644
--- a/SQL/0000-00-00-schema.sql
+++ b/SQL/0000-00-00-schema.sql
@@ -2096,7 +2096,9 @@ CREATE TABLE `data_release` (
`file_name` varchar(255),
`version` varchar(255),
`upload_date` date,
- PRIMARY KEY (`id`)
+ `ProjectID` INT(10) UNSIGNED NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (ProjectID) REFERENCES Project (ProjectID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `data_release_permissions` (
diff --git a/SQL/New_patches/2024-10-02-Data_Release_Project_separation.sql b/SQL/New_patches/2024-10-02-Data_Release_Project_separation.sql
new file mode 100644
index 00000000000..d067ce80fc8
--- /dev/null
+++ b/SQL/New_patches/2024-10-02-Data_Release_Project_separation.sql
@@ -0,0 +1,4 @@
+ALTER TABLE data_release
+ADD COLUMN ProjectID INT(10) UNSIGNED NULL DEFAULT NULL,
+ADD CONSTRAINT FK_ProjectID
+FOREIGN KEY (ProjectID) REFERENCES Project (ProjectID);
\ No newline at end of file
diff --git a/modules/data_release/jsx/addPermissionForm.js b/modules/data_release/jsx/addPermissionForm.js
index d4a5aa370de..5dd8a471a96 100644
--- a/modules/data_release/jsx/addPermissionForm.js
+++ b/modules/data_release/jsx/addPermissionForm.js
@@ -59,8 +59,7 @@ class AddPermissionForm extends Component {
* Called by React when the component has been rendered on the page.
*/
componentDidMount() {
- this.fetchData()
- .then(() => this.setState({isLoaded: true}));
+ this.fetchData().then(() => this.setState({isLoaded: true}));
}
/**
@@ -112,6 +111,7 @@ class AddPermissionForm extends Component {
errorMessage={this.state.errorMessage.Filename}
required={false}
value={this.state.formData.data_release_id}
+ autoSelect={false}
/>
OR
@@ -181,7 +182,6 @@ class AddPermissionForm extends Component {
}).then(function() {
window.location.assign('/data_release');
});
- this.props.fetchData();
} else {
let msg = response.statusText ?
response.statusText :
diff --git a/modules/data_release/jsx/dataReleaseIndex.js b/modules/data_release/jsx/dataReleaseIndex.js
index 22eb899fad0..efc1b7f8904 100644
--- a/modules/data_release/jsx/dataReleaseIndex.js
+++ b/modules/data_release/jsx/dataReleaseIndex.js
@@ -157,6 +157,11 @@ class DataReleaseIndex extends Component {
name: 'uploadDate',
type: 'text',
}},
+ {label: 'Project Name', show: true, filter: {
+ name: 'Project',
+ type: 'select',
+ options: this.state.data.fieldOptions.projects,
+ }},
{label: 'Data Release ID', show: false,
},
];
@@ -178,6 +183,7 @@ class DataReleaseIndex extends Component {
+ '/data_release/files'
}
action={loris.BaseURL + '/data_release/files'}
+ projects={this.state.data.fieldOptions.projects}
/>
);
diff --git a/modules/data_release/jsx/managePermissionsForm.js b/modules/data_release/jsx/managePermissionsForm.js
index 966921e476d..17bc3416e5d 100644
--- a/modules/data_release/jsx/managePermissionsForm.js
+++ b/modules/data_release/jsx/managePermissionsForm.js
@@ -7,6 +7,7 @@ import {
FormElement,
CheckboxElement,
StaticElement,
+ SearchableDropdown,
} from 'jsx/Form';
/**
@@ -24,11 +25,13 @@ class ManagePermissionsForm extends Component {
this.state = {
data: {},
+ originalData: {},
hasError: {},
errorMessage: {},
isLoaded: false,
};
+ this.setFormData = this.setFormData.bind(this);
this.fetchData = this.fetchData.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
@@ -49,7 +52,7 @@ class ManagePermissionsForm extends Component {
fetchData() {
return fetch(this.props.DataURL, {credentials: 'same-origin'})
.then((resp) => resp.json())
- .then((data) => this.setState({data}))
+ .then((data) => this.setState({data, originalData: data}))
.catch( (error) => {
this.setState({error: 'An error occurred when loading the form!'});
console.error(error);
@@ -88,26 +91,72 @@ class ManagePermissionsForm extends Component {
onSubmit={this.handleSubmit}
>
- {Object.entries(data).map(([userId, user]) => {
- const versions = Object.values(options.versions).map((version) =>
-
+
+ {this.state.user &&
+
- this.setFormData(userId, version, permission)
+ name={'versionsByUser'}
+ label={version}
+ value={data[this.state.user].versions.includes(version)}
+ onUserInput={(formElement, checked) =>
+ this.setFormData(
+ 'versionsByUser', {
+ userId: this.state.user, version, checked,
+ }
+ )
}
/>
- );
-
- return {versions} }
- />;
- })};
+ )}
+ />
+ }
+
+ {this.state.version &&
+ {
+ if (user.versions.includes(this.state.version)) {
+ return
+
+ this.setFormData(
+ 'usersByVersion',
+ {
+ userId: user.id,
+ checked,
+ version: this.state.version,
+ }
+ )
+ }
+ />
+ ;
+ }
+ }
+ )}
+ />
+ }
);
@@ -116,19 +165,25 @@ class ManagePermissionsForm extends Component {
/**
* Store the value of the element in this.state.data
*
- * @param {string} userId
- * @param {string} version
- * @param {boolean} permission
+ * @param {string} formElement - name of the selected element
+ * @param {string} value - selected value for corresponding form element
*/
- setFormData(userId, version, permission) {
+ setFormData(formElement, value) {
let {data} = JSON.parse(JSON.stringify(this.state));
- if (permission) {
- data[userId].versions = [...data[userId].versions, version];
+ if (formElement === 'versionsByUser' || formElement === 'usersByVersion') {
+ let {checked, version, userId} = value;
+ if (checked) {
+ data[userId].versions = [...data[userId].versions, version];
+ } else {
+ data[userId].versions = data[userId].versions
+ .filter((e) => e !== version);
+ }
+ this.setState({data});
} else {
- data[userId].versions = data[userId].versions
- .filter((e) => e !== version);
+ this.setState({[formElement]: (value === '' ? null : value)});
+ if (formElement != 'user') this.setState({user: null});
+ if (formElement != 'version') this.setState({version: null});
}
- this.setState({data});
}
/**
diff --git a/modules/data_release/jsx/uploadFileForm.js b/modules/data_release/jsx/uploadFileForm.js
index 7563e468c8b..0be347b2f5e 100644
--- a/modules/data_release/jsx/uploadFileForm.js
+++ b/modules/data_release/jsx/uploadFileForm.js
@@ -8,6 +8,7 @@ import {
FileElement,
TextboxElement,
ButtonElement,
+ SelectElement,
} from 'jsx/Form';
/**
@@ -79,6 +80,14 @@ class UploadFileForm extends Component {
required={false}
value={this.state.formData.version}
/>
+
@@ -130,6 +139,13 @@ class UploadFileForm extends Component {
return;
}
+ if (!formData.project) {
+ errorMessage.Project = 'You must select a project';
+ hasError.Project = true;
+ this.setState({errorMessage, hasError});
+ return;
+ }
+
// Check that the size of the file is not bigger than the allowed size
let fileSize = formData.file ? Math.round((formData.file.size/1024)) : null;
const maxSizeAllowed = this.state.data.maxUploadSize;
@@ -230,6 +246,7 @@ class UploadFileForm extends Component {
UploadFileForm.propTypes = {
DataURL: PropTypes.string.isRequired,
action: PropTypes.string.isRequired,
+ projects: PropTypes.array.isRequired,
};
export default UploadFileForm;
diff --git a/modules/data_release/php/data_release.class.inc b/modules/data_release/php/data_release.class.inc
index 6f8a814c5c1..47ffded4583 100644
--- a/modules/data_release/php/data_release.class.inc
+++ b/modules/data_release/php/data_release.class.inc
@@ -59,8 +59,12 @@ class Data_Release extends \DataFrameworkMenu
function getUsersList(\Database $DB)
{
return $DB->pselectColWithIndexKey(
- "SELECT ID, UserID FROM users",
- [],
+ "SELECT DISTINCT ID, LOWER(users.UserID) as UserID FROM users
+ JOIN user_project_rel upr ON upr.UserID = users.ID
+ WHERE upr.ProjectID IN
+ (SELECT ProjectID FROM user_project_rel WHERE UserID = :userID)
+ ORDER BY LOWER(users.UserID)",
+ [':userID' => \User::singleton()->getID()],
"ID"
);
}
@@ -75,8 +79,10 @@ class Data_Release extends \DataFrameworkMenu
function getFilesList(\Database $DB)
{
$result = $DB->pselectWithIndexKey(
- "SELECT id, file_name, version FROM data_release",
- [],
+ "SELECT id, file_name, version, ProjectID FROM data_release
+ WHERE ProjectID IS NULL OR ProjectID IN
+ (SELECT ProjectID FROM user_project_rel WHERE UserID = :userID)",
+ [':userID' => \User::singleton()->getID()],
"id"
);
@@ -99,9 +105,12 @@ class Data_Release extends \DataFrameworkMenu
*/
function getVersionsList(\Database $DB)
{
+ $user =& \User::singleton();
$versions = $DB->pselectCol(
- "SELECT DISTINCT version FROM data_release",
- [],
+ "SELECT DISTINCT version FROM data_release
+ WHERE ProjectID IS NULL OR ProjectID IN
+ (SELECT ProjectID FROM user_project_rel WHERE UserID = :userID)",
+ ['userID' => $user->getID()],
"version"
);
@@ -125,8 +134,10 @@ class Data_Release extends \DataFrameworkMenu
function getVersionFiles(\Database $db)
{
$result = $db->pselect(
- "SELECT version, id FROM data_release",
- []
+ "SELECT version, id FROM data_release
+ WHERE ProjectID IS NULL OR ProjectID IN
+ (SELECT ProjectID FROM user_project_rel WHERE UserID = :userID)",
+ [':userID' => \User::singleton()->getID()]
);
$versionFiles = [];
@@ -151,16 +162,23 @@ class Data_Release extends \DataFrameworkMenu
$result = $db->pselect(
"SELECT u.ID as userId,
u.UserID as userName,
- drp.data_release_id fileId
+ drp.data_release_id fileId,
+ dr.ProjectID as ProjectID
FROM users u
LEFT JOIN data_release_permissions drp ON (u.ID=drp.userid)
- LEFT JOIN data_release dr ON (drp.data_release_id=dr.id)",
- []
+ LEFT JOIN data_release dr ON (drp.data_release_id=dr.id)
+ JOIN user_project_rel upr ON upr.UserID = u.ID
+ WHERE upr.ProjectID IN
+ (SELECT ProjectID FROM user_project_rel WHERE UserID = :userID)",
+ [':userID' => \User::singleton()->getID()]
);
+ error_log(print_r($result, true));
+
$userFiles = [];
foreach ($result as $row) {
$userFiles[$row['userId']]['name'] = $row['userName'];
+ $userFiles[$row['userId']]['id'] = $row['userId'];
if (empty($userFiles[$row['userId']]['files'])) {
$userFiles[$row['userId']]['files'] = [];
}
@@ -206,11 +224,13 @@ class Data_Release extends \DataFrameworkMenu
*/
protected function getFieldOptions() : array
{
- $db = $this->loris->getDatabaseConnection();
+ $db = $this->loris->getDatabaseConnection();
+ $projects = \Utility::getProjectList();
return [
'users' => $this->getUsersList($db),
'versions' => $this->getVersionsList($db),
'filenames' => $this->getFilesList($db),
+ 'projects' => array_combine($projects, $projects)
];
}
diff --git a/modules/data_release/php/datareleaseprovisioner.class.inc b/modules/data_release/php/datareleaseprovisioner.class.inc
index 636f2b5c36f..f3eab13e526 100644
--- a/modules/data_release/php/datareleaseprovisioner.class.inc
+++ b/modules/data_release/php/datareleaseprovisioner.class.inc
@@ -44,8 +44,10 @@ class DataReleaseProvisioner extends \LORIS\Data\Provisioners\DBRowProvisioner
file_name AS fileName,
IF(version is null or version ='','Unversioned', version) AS version,
upload_date AS uploadDate,
+ p.Name as ProjectName,
dr.id as dataReleaseID
- FROM data_release dr";
+ FROM data_release dr
+ LEFT JOIN Project p on p.ProjectID = dr.ProjectID";
if (!$user->hasPermission("superuser")) {
$query .= "
@@ -54,7 +56,11 @@ class DataReleaseProvisioner extends \LORIS\Data\Provisioners\DBRowProvisioner
ON
(dr.id=drp.data_release_id)
WHERE
- drp.UserID=".$user->getID();
+ drp.UserID=".$user->getID()." AND (
+ dr.ProjectID IS NULL OR dr.ProjectID IN
+ (SELECT ProjectID FROM user_project_rel
+ WHERE UserID = ".$user->getID().")
+ )";
}
$query .= " ORDER BY uploadDate";
diff --git a/modules/data_release/php/files.class.inc b/modules/data_release/php/files.class.inc
index 4ac7cc31e36..b9067d51d37 100644
--- a/modules/data_release/php/files.class.inc
+++ b/modules/data_release/php/files.class.inc
@@ -116,6 +116,7 @@ class Files extends \NDB_Page
$request,
$posted['version'],
$overwrite,
+ $posted['project'],
);
}
@@ -180,15 +181,17 @@ class Files extends \NDB_Page
* Moves a to the appropriate place on the filesystem and inserts into
* the database, returning an appropriate HTTP response.
*
- * @param \LORIS\FilesUploadHandler $files The FilesUploadHandler which moves
- * the file and generates the
- * response.
- * @param \User $user The user uploading the file.
- * @param string $fileName The file name being uploaded.
- * @param ServerRequestInterface $request The incoming request.
- * @param ?string $version The user submitted file version.
- * @param bool $overwrite Flag to indicate if existing file
- * should be overwritten.
+ * @param \LORIS\FilesUploadHandler $files The FilesUploadHandler which
+ * moves the file and generates
+ * the response.
+ * @param \User $user The user uploading the file.
+ * @param string $fileName The file name being uploaded.
+ * @param ServerRequestInterface $request The incoming request.
+ * @param ?string $version The user submitted file version.
+ * @param bool $overwrite Flag to indicate if existing
+ * file should be overwritten.
+ * @param ?string $projectName The name of the project the file
+ * belongs to
*
* @return ResponseInterface
*/
@@ -198,13 +201,20 @@ class Files extends \NDB_Page
string $fileName,
ServerRequestInterface $request,
?string $version,
- bool $overwrite
+ bool $overwrite,
+ ?string $projectName
) : ResponseInterface {
$DB = $this->loris->getDatabaseConnection();
if ($version !== null) {
$version = strtolower($version);
}
+ // Get ProjectID
+ $ProjectID = $DB->pselectOne(
+ "SELECT ProjectID FROM Project WHERE Name=:project",
+ ['project' => $projectName]
+ );
+
$upload_date = date('Y-m-d');
if ($overwrite) {
// update file in data_release table.
@@ -213,6 +223,7 @@ class Files extends \NDB_Page
[
'version' => $version,
'upload_date' => $upload_date,
+ 'ProjectID' => $ProjectID,
],
['file_name' => $fileName]
);
@@ -224,6 +235,7 @@ class Files extends \NDB_Page
'file_name' => $fileName,
'version' => $version,
'upload_date' => $upload_date,
+ 'ProjectID' => $ProjectID,
]
);
diff --git a/raisinbread/RB_files/RB_data_release.sql b/raisinbread/RB_files/RB_data_release.sql
index ee9b87faa9b..53a07f1e90a 100644
--- a/raisinbread/RB_files/RB_data_release.sql
+++ b/raisinbread/RB_files/RB_data_release.sql
@@ -1,6 +1,6 @@
SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE `data_release`;
LOCK TABLES `data_release` WRITE;
-INSERT INTO `data_release` (`id`, `file_name`, `version`, `upload_date`) VALUES (1,'46-054.gif','','2018-05-07');
+INSERT INTO `data_release` (`id`, `file_name`, `version`, `upload_date`, `ProjectID`) VALUES (1,'46-054.gif','','2018-05-07', 1);
UNLOCK TABLES;
SET FOREIGN_KEY_CHECKS=1;
From 18bbf8f71c44c4b3812d447cb14b39528efc8296 Mon Sep 17 00:00:00 2001
From: Maxime Mulder
Date: Wed, 6 Nov 2024 13:29:29 -0500
Subject: [PATCH 20/28] Relax Typescript JSdoc linting configuration (#9437)
Relax the Typescript JSDoc linting configuration by doing the following:
- Remove the need for a `@returns` annotations, since the rest of the
documentation and the return type are often sufficient.
- Remove warnings for no `@param` and `@returns` type in the
documentation, since those are already provided by Typescript.
The doc comment for functions is still required and there is still a
warning when no `@param` is present.
---
.eslintrc.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.eslintrc.json b/.eslintrc.json
index 37500d5dcf0..5057ae7524b 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -111,8 +111,9 @@
}}
],
"jsdoc/require-param-description": "error",
- "jsdoc/require-returns": "error",
"jsdoc/require-returns-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns-type": "off",
"no-undef": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off"
From f9a8e528e02fe1eb06a5413b9841bb8a752bb2a6 Mon Sep 17 00:00:00 2001
From: Henri Rabalais
Date: Wed, 6 Nov 2024 19:39:00 +0100
Subject: [PATCH 21/28] [JSX] Refactor Modal Component to Improve UX and Code
Maintainability (#9439)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This refactors the Modal.js React component to enhance its usability,
simplify state management through hooks, and improve the user
experience. The updates streamline interactions with loading states and
successful submission feedback, while also updating the modal’s style
and structure for better flexibility.
### Key Changes
1. Functional Component Refactor: Converted Modal from a class-based to
a functional component, utilizing React hooks (useState) for managing
loading and success states.
2. Loading and Success Feedback:
- Integrated a Loader component to display a loading indicator during
form submission.
- Added a success message on form completion, which displays briefly
before automatically closing the modal.
3. Styling and Layout Improvements:
- Enhanced the modal’s layout, including more visually distinct header,
footer, and content sections.
- Adjusted styles dynamically based on state (loading and success) to
improve feedback clarity.
- Upated background overlay and animations for smoother transitions.
4. Improved Form Handling:
- Added a delay function (wait) for smooth feedback timing, allowing
users to visually confirm the success state.
5. Warnings and Prompts:
- Preserved the throwWarning functionality, utilizing Swal for
confirmation prompts before closing unsaved forms.
### Additional Notes
- Backward Compatibility: While these updates significantly refactor the
modal, they maintain backward compatibility with existing props
(onClose, onSubmit, throwWarning, etc.).
- Test Recommendations: Test form submissions under various states
(loading, success, with/without throwWarning) to confirm smooth
transitions and proper modal closure.
- Since I had to convert Loader.js to Loader.tsx to properly import it
in the Modal, I went ahead and intergrated the changes from #9400 into
this PR (it's just styling to change the boarder width based on the size
of the loader)
This refactor optimizes the Modal component, making it more efficient
and user-friendly while enhancing readability and maintainability.
---
jsx/Loader.js | 28 ---
jsx/Loader.tsx | 21 ++
jsx/Modal.d.ts | 48 ----
jsx/Modal.js | 185 ---------------
jsx/Modal.tsx | 216 ++++++++++++++++++
.../test/BatteryManagerTest.php | 95 ++++----
.../test/candidate_listTest.php | 16 +-
7 files changed, 295 insertions(+), 314 deletions(-)
delete mode 100644 jsx/Loader.js
create mode 100644 jsx/Loader.tsx
delete mode 100644 jsx/Modal.d.ts
delete mode 100644 jsx/Modal.js
create mode 100644 jsx/Modal.tsx
diff --git a/jsx/Loader.js b/jsx/Loader.js
deleted file mode 100644
index 0d441d19ccb..00000000000
--- a/jsx/Loader.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This file contains the React component for Loader
- *
- * @author Henri Rabalais
- * @version 1.0.0
- */
-import PropTypes from 'prop-types';
-
-/**
- * Loader is a React component which shows a spinner wheel while
- * something is loading.
- *
- * @param {array} props - The React props
- * @return {HTMLElement} - Loader React component
- */
-function Loader(props) {
- return (
-
- );
-}
-
-Loader.propTypes = {size: PropTypes.string};
-Loader.defaultProps = {size: '120'};
-
-export default Loader;
diff --git a/jsx/Loader.tsx b/jsx/Loader.tsx
new file mode 100644
index 00000000000..14eb6c1e6bf
--- /dev/null
+++ b/jsx/Loader.tsx
@@ -0,0 +1,21 @@
+interface LoaderProps {
+ size?: number;
+}
+
+/**
+ * Loader component renders a spinner wheel of a specified size.
+ *
+ * @param {LoaderProps} props - The properties for the Loader component
+ * @returns {JSX.Element} A div representing the loading spinner
+ */
+const Loader = ({size = 120}: LoaderProps) => {
+ const loaderStyle = {
+ width: size,
+ height: size,
+ borderWidth: size/15,
+ };
+
+ return ;
+};
+
+export default Loader;
diff --git a/jsx/Modal.d.ts b/jsx/Modal.d.ts
deleted file mode 100644
index 6cb1ba08e73..00000000000
--- a/jsx/Modal.d.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {ReactNode} from 'react';
-
-type ModalProps = {
- title?: string
- onSubmit: () => Promise ,
- onClose: () => void,
- show: boolean,
- throwWarning?: boolean,
- children: React.ReactNode,
- width?: string
-
-}
-
-/**
- * The Modal class. See Modal.js
- */
-class Modal {
- props: ModalProps
- state: any
- context: object
- refs: {[key: string]: ReactInstance}
-
- /**
- * Construct a new modal
- */
- constructor(props: ModalProps)
-
- /**
- * React lifecycle method
- *
- * @returns {ReactNode}
- */
- render(): ReactNode
-
- /**
- * React lifecycle method
- *
- * @param {object} newstate - the state to overwrite
- */
- setState(newstate: object): void
-
- /**
- * React lifecycle method
- */
- forceUpdate(): void
-}
-
-export default Modal;
diff --git a/jsx/Modal.js b/jsx/Modal.js
deleted file mode 100644
index 2aacf8cb958..00000000000
--- a/jsx/Modal.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * This file contains the React Component for a Modal Window.
- *
- * @author Henri Rabalais
- * @version 1.1.0
- */
-import React, {Component} from 'react';
-import PropTypes from 'prop-types';
-import swal from 'sweetalert2';
-import {ButtonElement} from 'jsx/Form';
-
-/**
- * Modal Component.
- * React wrapper for a Modal Window. Allows to dynamically toggle a Modal
- * window.
- *
- * ================================================
- * Usage:
- * - Wrap the contents to be displayed by the Modal Window by the
- * Modal Component.
- * - Use the 'title' prop to set a title for the Modal Component.
- * - Use the 'onSubmit' prop to set a submission *promise* object for the
- * Modal's contents.
- * - Use the 'onClose' prop to set a function that triggers upon Modal closure.
- * - Use the 'throwWarning' prop to throw a warning upon closure of the
- * Modal Window.
- * =================================================
- *
- */
-class Modal extends Component {
- /**
- * @constructor
- */
- constructor() {
- super();
- this.handleClose = this.handleClose.bind(this);
- }
-
- /**
- * Display a warning message on close
- */
- handleClose() {
- if (this.props.throwWarning) {
- swal.fire({
- title: 'Are You Sure?',
- text: 'Leaving the form will result in the loss of any information ' +
- 'entered.',
- type: 'warning',
- showCancelButton: true,
- confirmButtonText: 'Proceed',
- cancelButtonText: 'Cancel',
- }).then((result) => result.value && this.props.onClose());
- } else {
- this.props.onClose();
- }
- }
-
- /**
- * Renders the React component.
- *
- * @return {JSX} - React markup for the component
- */
- render() {
- const {show, children, onSubmit, title, width} = this.props;
-
- const headerStyle = {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- height: '40px',
- borderTopRightRadius: '10',
- fontSize: 24,
- padding: 35,
- borderBottom: '1px solid #DDDDDD',
- };
-
- const glyphStyle = {
- marginLeft: 'auto',
- cursor: 'pointer',
- };
-
- const bodyStyle = {
- padding: 15,
- maxHeight: '75vh',
- overflowY: 'scroll',
- };
-
- const modalContainer = {
- display: 'block',
- position: 'fixed',
- zIndex: 9999,
- paddingTop: '65px',
- left: 0,
- top: 0,
- width: '100%',
- height: '100%',
- overflow: 'auto',
- backgroundColor: 'rgba(0,0,0,0.7)',
- visibility: show ? 'visible' : 'hidden',
- };
-
- const modalContent = {
- opacity: show ? 1 : 0,
- top: show ? 0 : '-300px',
- position: 'relative',
- backgroundColor: '#fefefe',
- borderRadius: '7px',
- margin: 'auto',
- padding: 0,
- border: '1px solid #888',
- width: width || '700px',
- boxShadow: '0 4px 8px 0 rbga(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)',
- transition: 'top 0.4s, opacity 0.4s',
- };
-
- const renderChildren = () => show && children;
-
- const footerStyle = {
- borderTop: '1px solid #DDDDDD',
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'center',
- height: '40px',
- padding: '35px 35px 20px 35px',
- };
-
- const submitStyle = {
- marginLeft: 'auto',
- marginRight: '20px',
- };
-
- const submitButton = () => {
- if (onSubmit) {
- const submit = () => onSubmit().then(() => this.props.onClose())
- .catch(() => {});
- return (
-
-
-
- );
- }
- };
-
- return (
-
- e.stopPropagation()}
- >
-
- {title}
-
- ×
-
-
-
- {renderChildren()}
-
-
- {submitButton()}
-
-
-
- );
- }
-}
-
-Modal.propTypes = {
- title: PropTypes.string,
- onSubmit: PropTypes.func,
- onClose: PropTypes.func.isRequired,
- show: PropTypes.bool.isRequired,
- throwWarning: PropTypes.bool,
- children: PropTypes.node,
- width: PropTypes.string,
-};
-
-Modal.defaultProps = {
- throwWarning: false,
-};
-
-export default Modal;
diff --git a/jsx/Modal.tsx b/jsx/Modal.tsx
new file mode 100644
index 00000000000..ca7f683d711
--- /dev/null
+++ b/jsx/Modal.tsx
@@ -0,0 +1,216 @@
+import {useState, PropsWithChildren, CSSProperties} from 'react';
+import Swal from 'sweetalert2';
+import Loader from './Loader';
+import {
+ ButtonElement,
+} from 'jsx/Form';
+
+type ModalProps = PropsWithChildren<{
+ throwWarning?: boolean;
+ show: boolean;
+ onClose: () => void;
+ onSubmit?: () => Promise;
+ onSuccess?: (data: any) => void;
+ title?: string;
+}>;
+
+/**
+ * Modal Component
+ *
+ * A React functional component that renders a modal dialog with optional
+ * form submission and loading indicators. Supports asynchronous form submission
+ * with loading and success feedback.
+ *
+ * @param {ModalProps} props - Properties for the modal component
+ * @returns {JSX.Element} - A modal dialog box w/ optional submit functionality
+ */
+const Modal = ({
+ throwWarning = false,
+ show = false,
+ onClose,
+ onSubmit,
+ onSuccess,
+ title,
+ children,
+}: ModalProps) => {
+ const [loading, setLoading] = useState(false); // Tracks loading during submit
+ const [success, setSuccess] = useState(false); // Tracks success after submit
+
+ /**
+ * Handles modal close event. Shows a confirmation if `throwWarning` is true.
+ */
+ const handleClose = () => {
+ if (throwWarning) { // Display warning if enabled
+ Swal.fire({
+ title: 'Are You Sure?',
+ text: 'Leaving the form will result in the loss of any information ' +
+ 'entered.',
+ type: 'warning',
+ showCancelButton: true,
+ confirmButtonText: 'Proceed',
+ cancelButtonText: 'Cancel',
+ }).then((result) => result.value && onClose());
+ } else {
+ onClose(); // Close immediately if no warning
+ }
+ };
+
+ /**
+ * Manages form submission with loading and success states, calling
+ * `onSubmit` and handling modal state based on success or failure.
+ */
+ const handleSubmit = async () => {
+ if (!onSubmit) return; // Ensure onSubmit exists
+
+ setLoading(true); // Show loader
+
+ try {
+ const data = await onSubmit();
+ setLoading(false);
+ setSuccess(true); // Show success
+
+ await new Promise((resolve) => setTimeout(resolve, 2000)); // Close delay
+
+ setSuccess(false); // Reset success state
+ onClose(); // Close modal
+ onSuccess?.(data); // call onSuccess if defined
+ } catch {
+ setLoading(false);
+ }
+ };
+
+ /**
+ * Renders submit button if `onSubmit` is provided and no loading or success.
+ *
+ * @returns {JSX.Element | undefined} - The submit button if conditions are met
+ */
+ const submitButton = () => {
+ if (onSubmit && !(loading || success)) { // Show button if conditions met
+ return (
+
+
+
+ );
+ }
+ };
+
+ const headerStyle: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ height: '40px',
+ borderTopRightRadius: '10',
+ fontSize: 24,
+ padding: 35,
+ borderBottom: '1px solid #DDDDDD',
+ };
+
+ const glyphStyle: CSSProperties = {
+ marginLeft: 'auto',
+ cursor: 'pointer',
+ };
+
+ const bodyStyle: CSSProperties = {
+ padding: success ? 0 : '15px 15px',
+ maxHeight: success ? 0 : '75vh',
+ overflow: 'scroll',
+ opacity: success ? 0 : 1,
+ transition: '1s ease, opacity 0.3s',
+ };
+
+ const modalContainer: CSSProperties = {
+ display: 'block',
+ position: 'fixed',
+ zIndex: 9999,
+ paddingTop: '100px',
+ paddingBottom: '100px',
+ left: 0,
+ top: 0,
+ width: '100%',
+ height: '100%',
+ overflow: 'auto',
+ backgroundColor: 'rgba(0,0,0,0.7)',
+ visibility: show ? 'visible' : 'hidden',
+ };
+
+ const modalContent: CSSProperties = {
+ opacity: show ? 1 : 0,
+ top: show ? 0 : '-300px',
+ position: 'relative',
+ backgroundColor: '#fefefe',
+ borderRadius: '7px',
+ margin: 'auto',
+ padding: 0,
+ border: '1px solid #888',
+ width: '700px',
+ boxShadow: '0 4px 8px 0 rbga(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19)',
+ transition: '0.4s ease',
+ };
+
+ const footerStyle: CSSProperties = {
+ borderTop: '1px solid #DDDDDD',
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ height: '40px',
+ padding: '35px',
+ backgroundColor: success ? '#e0ffec' : undefined,
+ };
+
+ const submitStyle: CSSProperties = {
+ marginLeft: 'auto',
+ marginRight: '20px',
+ };
+
+ const processStyle: CSSProperties = {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-evenly',
+ margin: '0px auto',
+ width: '90px',
+ };
+
+ /**
+ * Loader element displayed during form submission.
+ */
+ const loader = loading && (
+
+
+ Saving
+
+ );
+
+ /**
+ * Success display element shown after successful form submission.
+ */
+ const successDisplay = success && (
+
+
+ Success!
+
+ );
+
+ return (
+
+ e.stopPropagation()}>
+
+ {title}
+ ×
+
+
+ {show && children}
+
+ {loader}
+ {successDisplay}
+ {submitButton()}
+
+
+
+
+ );
+};
+
+export default Modal;
diff --git a/modules/battery_manager/test/BatteryManagerTest.php b/modules/battery_manager/test/BatteryManagerTest.php
index 06c482854d7..ac2775621bf 100644
--- a/modules/battery_manager/test/BatteryManagerTest.php
+++ b/modules/battery_manager/test/BatteryManagerTest.php
@@ -170,50 +170,45 @@ function testEditform()
);
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace > div>div:nth-child(2)>div>div:nth-child(2)>form>".
- " div > div:nth-child(2) > div > div > select > option:nth-child(2)"
- )
+ "#lorisworkspace > div:nth-child(1) > div:nth-child(2) > ".
+ "div:nth-child(1) > div:nth-child(2) > div:nth-child(1) >".
+ " form:nth-child(1) > div:nth-child(1) >div:nth-child(2) >".
+ " div:nth-child(1) > div:nth-child(2) > select:nth-child(1) >" .
+ " option:nth-child(3)"
+ ),
+ 5
);
-
$this->safeFindElement(
WebDriverBy::cssSelector(
- "#lorisworkspace > div> div:nth-child(2) > div > div:nth-child(2)".
- " > form > div > div:nth-child(3) > div > div > input"
+ "div:nth-child(3) > div:nth-child(1) >".
+ " div:nth-child(2) > input:nth-child(1)",
+ 1
)
)->clear()->sendKeys('0');
+
$this->safeFindElement(
WebDriverBy::cssSelector(
- "#lorisworkspace>div> div:nth-child(2) > div > div:nth-child(2) ".
- "> form > div > div:nth-child(4) > div > div > input"
- )
+ "div.col-sm-12:nth-child(4)>div:nth-child(1)".
+ ">div:nth-child(2)>input:nth-child(1)"
+ ),
+ 1
)->clear()->sendKeys('1');
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace>div>div:nth-child(2)>div>div:nth-child(2)>form>".
- "div>div:nth-child(5) > div > div > select > option:nth-child(7)"
- )
- );
- $this->safeClick(
- WebDriverBy::cssSelector(
- "#lorisworkspace > div > div:nth-child(2) > div > div:nth-child(2)>".
- "form>div>div:nth-child(6) > div > div >select> option:nth-child(2)"
- )
+ "div:nth-child(5) > div:nth-child(1) > div:nth-child(2) > ".
+ "select:nth-child(1) > option:nth-child(2)"
+ ),
+ 1
);
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace > div >div:nth-child(2) > div > div:nth-child(2) >".
- "form>div>div:nth-child(7) >div > div > select > option:nth-child(5)"
- )
- );
-
- $this->safeClick(
- WebDriverBy::cssSelector(
- "#lorisworkspace>div>div:nth-child(2)>div>div:nth-child(2)>form>".
- " div > div:nth-child(12) > div > div > button"
- )
+ "div.col-sm-9:nth-child(1) > button:nth-child(1)"
+ ),
+ 1
);
$bodyText = $this->safeFindElement(
- WebDriverBy::cssSelector("#swal2-title")
+ WebDriverBy::cssSelector("#swal2-title"),
+ 1
)->getText();
$this->assertStringContainsString(
"Submission successful!",
@@ -238,37 +233,47 @@ function testAddNew()
);
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace > div > div:nth-child(2) > div > div:nth-child(2) ".
- "> form > div > div:nth-child(2)>div>div>select>option:nth-child(2)"
- )
+ "#lorisworkspace > div:nth-child(1) > div:nth-child(2) >".
+ " div:nth-child(1) > div:nth-child(2) > div:nth-child(1) >".
+ " form:nth-child(1) > div:nth-child(1) >div:nth-child(2) > ".
+ "div:nth-child(1) > div:nth-child(2) > select:nth-child(1) >".
+ " option:nth-child(3)"
+ ),
+ 5
);
$this->safeFindElement(
WebDriverBy::cssSelector(
- "#lorisworkspace > div > div:nth-child(2) > div ".
- "> div:nth-child(2)>form>div>div:nth-child(3) > div > div > input"
+ "div:nth-child(3) > div:nth-child(1) > ".
+ "div:nth-child(2) > input:nth-child(1)",
+ 1
)
)->clear()->sendKeys('0');
+
$this->safeFindElement(
WebDriverBy::cssSelector(
- "#lorisworkspace > div > div:nth-child(2) > div >".
- " div:nth-child(2)>form > div > div:nth-child(4) > div > div >input"
- )
+ "div.col-sm-12:nth-child(4)>div:nth-child(1)>".
+ "div:nth-child(2) >input:nth-child(1)"
+ ),
+ 1
)->clear()->sendKeys('1');
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace > div > div:nth-child(2)>div>div:nth-child(2)>form".
- " > div > div:nth-child(5) > div > div >select > option:nth-child(7)"
- )
+ "div:nth-child(5) > div:nth-child(1) > div:nth-child(2) >".
+ " select:nth-child(1) > option:nth-child(2)
+"
+ ),
+ 1
);
$this->safeClick(
WebDriverBy::cssSelector(
- "#lorisworkspace > div >div:nth-child(2)>div>div:nth-child(2)>form ".
- "> div > div:nth-child(12) > div > div > button"
- )
+ "div.col-sm-9:nth-child(1) > button:nth-child(1)"
+ ),
+ 1
);
$bodyText = $this->safeFindElement(
- WebDriverBy::cssSelector("#swal2-title")
+ WebDriverBy::cssSelector("#swal2-title"),
+ 1
)->getText();
$this->assertStringContainsString(
"Submission successful!",
@@ -312,7 +317,7 @@ function testFilter()
self::$display,
self::$clearFilter,
'AOSI',
- '4 rows'
+ '2 rows'
);
$this->_filterTest(
self::$minimumAge,
diff --git a/modules/candidate_list/test/candidate_listTest.php b/modules/candidate_list/test/candidate_listTest.php
index 3e41b9bcaa3..16995cd86b2 100644
--- a/modules/candidate_list/test/candidate_listTest.php
+++ b/modules/candidate_list/test/candidate_listTest.php
@@ -309,22 +309,22 @@ function testDataEntryAndOpenProfile()
$this->safeGet($this->url . "/candidate_list/");
//click open profile button
$btn = self::$openProfile;
- $this->safeClick(WebDriverBy::cssSelector($btn));
+ $this->safeClick(WebDriverBy::cssSelector($btn), 1);
// input PSCID and DCCID
- $dccid = "#lorisworkspace > div > div:nth-child(1) > div >".
- " div:nth-child(2)>form>div>div:nth-child(1)>div>div>input";
- $pscid = "#lorisworkspace > div > div:nth-child(1) > div >".
- " div:nth-child(2)>form>div>div:nth-child(2)>div>div>input";
+ $dccid = 'input[name="CandID"]:nth-child(1)';
+ $pscid = 'input[name="PSCID"]:nth-child(1)';
// to do react input value
$this->safeFindElement(
- WebDriverBy::cssSelector($dccid)
+ WebDriverBy::cssSelector($dccid),
+ 1
)->sendKeys('300001');
$this->safeFindElement(
- WebDriverBy::cssSelector($pscid)
+ WebDriverBy::cssSelector($pscid),
+ 1
)->sendKeys('MTL001');
$btn = ".col-sm-12 > .row .btn";
//to do check the url
- $this->safeClick(WebDriverBy::cssSelector($btn));
+ $this->safeClick(WebDriverBy::cssSelector($btn), 1);
sleep(2);
$URL = $this->webDriver->executeScript("return window.location.href;");
$this->assertStringContainsString("300001", $URL);
From 04450c4bc172cfec349a9ee9d4d16efc8ffb19e8 Mon Sep 17 00:00:00 2001
From: Ayush Bhardwaj <96904351+ay-bh@users.noreply.github.com>
Date: Thu, 7 Nov 2024 00:19:01 +0530
Subject: [PATCH 22/28] [issue_tracker] Add Instrument dropdown to Issue form
(#9311)
## Brief summary of changes
Added 'Instrument' dropdown field to Issue Tracker
* Resolves #8896
---
SQL/0000-00-00-schema.sql | 9 ++++--
...suetracker_AddsInstrumentToIssuesTable.sql | 11 ++++++++
modules/issue_tracker/jsx/IssueForm.js | 13 +++++++++
modules/issue_tracker/php/edit.class.inc | 26 +++++++++++++++--
raisinbread/RB_files/RB_issues.sql | 28 +++++++++----------
5 files changed, 68 insertions(+), 19 deletions(-)
create mode 100644 SQL/New_patches/2024-07-19-issuetracker_AddsInstrumentToIssuesTable.sql
diff --git a/SQL/0000-00-00-schema.sql b/SQL/0000-00-00-schema.sql
index d6c8a1ddce0..deabd1eff4f 100644
--- a/SQL/0000-00-00-schema.sql
+++ b/SQL/0000-00-00-schema.sql
@@ -1529,6 +1529,7 @@ CREATE TABLE `issues` (
`candID` int(6) DEFAULT NULL,
`category` varchar(255) DEFAULT NULL,
`description` longtext DEFAULT NULL,
+ `instrument` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`issueID`),
KEY `fk_issues_1` (`reporter`),
KEY `fk_issues_2` (`assignee`),
@@ -1537,20 +1538,22 @@ CREATE TABLE `issues` (
KEY `fk_issues_5` (`centerID`),
KEY `fk_issues_6` (`lastUpdatedBy`),
KEY `fk_issues_8` (`category`),
+ KEY `fk_issues_instrument` (`instrument`),
CONSTRAINT `fk_issues_8` FOREIGN KEY (`category`) REFERENCES `issues_categories` (`categoryName`),
CONSTRAINT `fk_issues_1` FOREIGN KEY (`reporter`) REFERENCES `users` (`UserID`),
CONSTRAINT `fk_issues_2` FOREIGN KEY (`assignee`) REFERENCES `users` (`UserID`),
CONSTRAINT `fk_issues_3` FOREIGN KEY (`candID`) REFERENCES `candidate` (`CandID`),
CONSTRAINT `fk_issues_4` FOREIGN KEY (`sessionID`) REFERENCES `session` (`ID`),
CONSTRAINT `fk_issues_5` FOREIGN KEY (`centerID`) REFERENCES `psc` (`CenterID`),
- CONSTRAINT `fk_issues_6` FOREIGN KEY (`lastUpdatedBy`) REFERENCES `users` (`UserID`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ CONSTRAINT `fk_issues_6` FOREIGN KEY (`lastUpdatedBy`) REFERENCES `users` (`UserID`),
+ CONSTRAINT `fk_issues_instrument` FOREIGN KEY (`instrument`) REFERENCES `test_names` (`ID`) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `issues_history` (
`issueHistoryID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`newValue` longtext NOT NULL,
`dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `fieldChanged` enum('assignee','status','comment','sessionID','centerID','title','category','module','lastUpdatedBy','priority','candID', 'description','watching') NOT NULL DEFAULT 'comment',
+ `fieldChanged` enum('assignee','status','comment','sessionID','centerID','title','category','module','lastUpdatedBy','priority','candID', 'description','watching','instrument') NOT NULL DEFAULT 'comment',
`issueID` int(11) unsigned NOT NULL,
`addedBy` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`issueHistoryID`),
diff --git a/SQL/New_patches/2024-07-19-issuetracker_AddsInstrumentToIssuesTable.sql b/SQL/New_patches/2024-07-19-issuetracker_AddsInstrumentToIssuesTable.sql
new file mode 100644
index 00000000000..e0a8045d543
--- /dev/null
+++ b/SQL/New_patches/2024-07-19-issuetracker_AddsInstrumentToIssuesTable.sql
@@ -0,0 +1,11 @@
+ALTER TABLE `issues`
+ ADD `instrument` int(10) unsigned DEFAULT NULL
+ AFTER `description`;
+
+ALTER TABLE `issues`
+ ADD CONSTRAINT `fk_issues_instrument`
+ FOREIGN KEY (`instrument`) REFERENCES `test_names` (`ID`)
+ ON DELETE RESTRICT ON UPDATE CASCADE;
+
+ALTER TABLE `issues_history`
+ MODIFY `fieldChanged` enum('assignee','status','comment','sessionID','centerID','title','category','module','lastUpdatedBy','priority','candID', 'description','watching','instrument') NOT NULL DEFAULT 'comment';
diff --git a/modules/issue_tracker/jsx/IssueForm.js b/modules/issue_tracker/jsx/IssueForm.js
index 797a0129a5d..231d99310be 100644
--- a/modules/issue_tracker/jsx/IssueForm.js
+++ b/modules/issue_tracker/jsx/IssueForm.js
@@ -283,6 +283,15 @@ class IssueForm extends Component {
value={this.state.formData.priority}
sortByValue={false}
/>
+
getLongName();
}
+ $instruments = \NDB_BVL_Instrument::getInstrumentNamesList($this->loris);
+
// Now get issue values
$issueData = $this->getIssueData(null, $user);
if (!empty($values['issueID'])
@@ -271,6 +273,7 @@ class Edit extends \NDB_Page implements ETagCalculator
'priorities' => $priorities,
'categories' => $categories,
'modules' => $modules,
+ 'instruments' => $instruments,
'otherWatchers' => $otherWatchers,
'issueData' => $issueData,
'hasEditPermission' => $user->hasPermission(
@@ -381,11 +384,13 @@ class Edit extends \NDB_Page implements ETagCalculator
"SELECT
i.*,
c.PSCID,
- s.Visit_label AS visitLabel
+ s.Visit_label AS visitLabel,
+ t.Test_name as instrument_name
FROM
issues AS i
LEFT JOIN candidate c ON (i.candID = c.CandID)
LEFT JOIN session s ON (i.sessionID = s.ID)
+ LEFT JOIN test_names t ON (i.instrument = t.ID)
WHERE
issueID = :issueID",
['issueID' => $issueID]
@@ -423,6 +428,7 @@ class Edit extends \NDB_Page implements ETagCalculator
'visitLabel' => null,
'category' => null,
'lastUpdatedBy' => null,
+ 'instrument' => null,
];
}
@@ -485,6 +491,12 @@ class Edit extends \NDB_Page implements ETagCalculator
) {
$comment['newValue']
= $this->formatUserInformation($comment['newValue']);
+ } else if ($comment['fieldChanged'] === 'instrument') {
+ $instrumentName = $db->pselectOne(
+ "SELECT Test_name FROM test_names WHERE ID=:id",
+ ['id' => $comment['newValue']]
+ );
+ $comment['newValue'] = $instrumentName;
}
}
return $unformattedComments; //now formatted I guess
@@ -584,6 +596,7 @@ class Edit extends \NDB_Page implements ETagCalculator
'description',
'category',
'module',
+ 'instrument',
];
$fieldsToValidateFirst = [
'PSCID',
@@ -600,7 +613,16 @@ class Edit extends \NDB_Page implements ETagCalculator
$value = null;
}
if (isset($field)) {
- $issueValues[$field] = $value;
+ if ($field === 'instrument' && $value !== null) {
+ // Value coming from frontend will be the instrument name
+ $instrumentID = $db->pselectOne(
+ "SELECT ID FROM test_names WHERE Test_name=:name",
+ ['name' => $value]
+ );
+ $issueValues[$field] = $instrumentID;
+ } else {
+ $issueValues[$field] = $value;
+ }
}
}
foreach ($fieldsToValidateFirst as $vField) {
diff --git a/raisinbread/RB_files/RB_issues.sql b/raisinbread/RB_files/RB_issues.sql
index c667421673a..ea8ba1a544c 100644
--- a/raisinbread/RB_files/RB_issues.sql
+++ b/raisinbread/RB_files/RB_issues.sql
@@ -1,19 +1,19 @@
SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE `issues`;
LOCK TABLES `issues` WRITE;
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (14,'Need form added','admin','admin','closed','high',NULL,'2016-09-01 10:47:54','2016-09-01 18:49:55','admin',NULL,2,300001,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (15,'Need form added','admin','admin','new','low',NULL,'2016-09-01 10:51:46','2016-09-02 20:28:38','admin',NULL,2,300002,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (16,'new issue to test assignee notification','admin','admin','new','low',16,'2016-09-02 12:30:38','2020-02-27 22:17:07','admin',NULL,2,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (17,'another ticket to test assignment notification','admin','admin','new','low',NULL,'2016-09-02 12:31:45','2019-06-11 13:59:53','admin',NULL,2,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (18,'ticket 3 testing email','admin','admin','new','low',NULL,'2016-09-02 12:33:47','2019-06-11 14:00:01','admin',NULL,3,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (19,'testing if assignee gets email - new ticket','admin','admin','feedback','urgent',17,'2016-09-02 13:59:55','2020-02-27 22:17:07','admin',NULL,3,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (20,'testing if assignee gets email upon ticket update+re-assign','admin','admin','feedback','urgent',NULL,'2016-09-02 14:02:29','2020-02-27 22:17:07','admin',NULL,4,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (21,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:45:39','2019-06-11 14:00:19','admin',NULL,2,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (22,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:32','2019-06-11 14:00:24','admin',NULL,2,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (23,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:48','2016-09-02 23:46:48','admin',NULL,3,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (24,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:56','2019-06-11 14:00:31','admin',NULL,3,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (25,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:47:01','2019-06-11 14:00:36','admin',NULL,4,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (26,'testing site population','admin','admin','new','low',17,'2016-09-06 17:19:20','2020-02-27 22:17:07','admin',NULL,2,NULL,NULL,NULL);
-INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`) VALUES (27,'test ticket with visit label','admin','admin','new','low',9,'2016-09-06 17:38:06','2020-02-27 22:17:07','admin',2,2,300002,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (14,'Need form added','admin','admin','closed','high',NULL,'2016-09-01 10:47:54','2016-09-01 18:49:55','admin',NULL,2,300001,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (15,'Need form added','admin','admin','new','low',NULL,'2016-09-01 10:51:46','2016-09-02 20:28:38','admin',NULL,2,300002,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (16,'new issue to test assignee notification','admin','admin','new','low',16,'2016-09-02 12:30:38','2020-02-27 22:17:07','admin',NULL,2,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (17,'another ticket to test assignment notification','admin','admin','new','low',NULL,'2016-09-02 12:31:45','2019-06-11 13:59:53','admin',NULL,2,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (18,'ticket 3 testing email','admin','admin','new','low',NULL,'2016-09-02 12:33:47','2019-06-11 14:00:01','admin',NULL,3,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (19,'testing if assignee gets email - new ticket','admin','admin','feedback','urgent',17,'2016-09-02 13:59:55','2020-02-27 22:17:07','admin',NULL,3,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (20,'testing if assignee gets email upon ticket update+re-assign','admin','admin','feedback','urgent',NULL,'2016-09-02 14:02:29','2020-02-27 22:17:07','admin',NULL,4,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (21,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:45:39','2019-06-11 14:00:19','admin',NULL,2,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (22,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:32','2019-06-11 14:00:24','admin',NULL,2,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (23,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:48','2016-09-02 23:46:48','admin',NULL,3,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (24,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:46:56','2019-06-11 14:00:31','admin',NULL,3,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (25,'ticket to end all tickets ','admin','admin','new','low',NULL,'2016-09-02 15:47:01','2019-06-11 14:00:36','admin',NULL,4,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (26,'testing site population','admin','admin','new','low',17,'2016-09-06 17:19:20','2020-02-27 22:17:07','admin',NULL,2,NULL,NULL,NULL,NULL);
+INSERT INTO `issues` (`issueID`, `title`, `reporter`, `assignee`, `status`, `priority`, `module`, `dateCreated`, `lastUpdate`, `lastUpdatedBy`, `sessionID`, `centerID`, `candID`, `category`, `description`, `instrument`) VALUES (27,'test ticket with visit label','admin','admin','new','low',9,'2016-09-06 17:38:06','2020-02-27 22:17:07','admin',2,2,300002,NULL,NULL,NULL);
UNLOCK TABLES;
SET FOREIGN_KEY_CHECKS=1;
From 4860adfa212a3301baed2b9e775e099abec01b7b Mon Sep 17 00:00:00 2001
From: Henri Rabalais
Date: Wed, 6 Nov 2024 20:03:31 +0100
Subject: [PATCH 23/28] [JSX] Remove `hasError` Boolean from Form Elements
(#9447)
This removes the `hasError` boolean prop from various form
elements and updates the error handling logic to rely solely on the
`errorMessage` prop. This change simplifies the error handling mechanism
and ensures consistency across all form components.
### Changes Made
- **Removed `hasError` prop** from form elements in `jsx/Form.js`,
`jsx/Form.d.ts`, and other related files.
- **Updated form components** to check for the presence of
`errorMessage` instead of `hasError` to determine if an error state
should be displayed.
- **Modified default props and prop types** to remove `hasError` and
ensure `errorMessage` is used appropriately.
- **Adjusted form usage** in various modules to reflect this change,
including:
- `battery_manager/jsx/batteryManagerForm.js`
- `conflict_resolver/jsx/fix_conflict_form.js`
- `data_release/jsx/addPermissionForm.js`
- `data_release/jsx/managePermissionsForm.js`
- `data_release/jsx/uploadFileForm.js`
- `document_repository/jsx/categoryForm.js`
- `document_repository/jsx/deleteCategoryForm.js`
- `document_repository/jsx/editCategoryForm.js`
- `document_repository/jsx/editForm.js`
- `document_repository/jsx/uploadForm.js`
- `electrophysiology_uploader/jsx/UploadForm.js`
- `imaging_uploader/jsx/UploadForm.js`
- `issue_tracker/jsx/IssueForm.js`
- `issue_tracker/jsx/IssueTrackerBatchMode.js`
- `media/jsx/uploadForm.js`
- `genomic_browser/jsx/tabs_content/filemanager/uploadForm.js`
- `modules/form/DateTimePartialElement.tsx`
- **Ensured error messages** are displayed based on the presence of an
`errorMessage` prop, simplifying the error handling logic.
## Reason for Change
- **Simplifies error handling** by removing redundant `hasError` boolean
prop.
- **Reduces complexity** in form components by relying on a single prop
(`errorMessage`) for error states.
- **Improves consistency** across all form components in how errors are
managed and displayed.
---
jsx/Form.d.ts | 7 ++--
jsx/Form.js | 35 ++++++++----------
jsx/form/DateTimePartialElement.tsx | 3 +-
.../battery_manager/jsx/batteryManagerForm.js | 4 ---
.../jsx/fix_conflict_form.js | 9 +++--
modules/data_release/jsx/addPermissionForm.js | 15 +-------
.../data_release/jsx/managePermissionsForm.js | 1 -
modules/data_release/jsx/uploadFileForm.js | 15 ++------
.../document_repository/jsx/categoryForm.js | 1 -
.../jsx/deleteCategoryForm.js | 7 ++--
.../jsx/editCategoryForm.js | 14 ++++----
modules/document_repository/jsx/editForm.js | 1 -
modules/document_repository/jsx/uploadForm.js | 1 -
.../jsx/UploadForm.js | 30 ++--------------
.../tabs_content/filemanager/uploadForm.js | 1 -
modules/imaging_uploader/jsx/UploadForm.js | 36 +++++--------------
modules/issue_tracker/jsx/IssueForm.js | 2 +-
.../jsx/IssueTrackerBatchMode.js | 8 +++++
modules/media/jsx/uploadForm.js | 3 +-
19 files changed, 57 insertions(+), 136 deletions(-)
diff --git a/jsx/Form.d.ts b/jsx/Form.d.ts
index 3b57ec16c13..4b34465e99a 100644
--- a/jsx/Form.d.ts
+++ b/jsx/Form.d.ts
@@ -108,8 +108,7 @@ type selectElementProps = {
required?: boolean
emptyOption?: boolean
autoSelect?: boolean
- hasError?: boolean
- errorMessage?: boolean
+ errorMessage?: string
onUserInput: (name: string, value: any) => void
noMargins?: boolean
placeholder?: string
@@ -306,7 +305,6 @@ type fileElementProps = {
disabled?: boolean
required?: boolean
allowMultiple?: boolean
- hasError?: boolean
errorMessage?: string
onUserInput: (name: string, value: any) => void
};
@@ -407,7 +405,6 @@ type dateElementProps = {
dateFormat?: string
disabled?: boolean
required?: boolean
- hasError?: boolean
errorMessage?: string
onUserInput: (name: string, value: any) => void
};
@@ -595,7 +592,7 @@ type radioElementProps = {
required?: boolean
vertical?: boolean
checked: boolean
- errorMessage?: boolean
+ errorMessage?: string
elementClass?: boolean
onUserInput: (name: string, value: any) => void
}
diff --git a/jsx/Form.js b/jsx/Form.js
index 79795f578d6..08efe2eacde 100644
--- a/jsx/Form.js
+++ b/jsx/Form.js
@@ -451,7 +451,7 @@ SearchableDropdown.defaultProps = {
disabled: false,
required: false,
sortByValue: true,
- errorMessage: '',
+ errorMessage: null,
placeHolder: '',
onUserInput: function() {
console.warn('onUserInput() callback is not set');
@@ -554,7 +554,7 @@ export class SelectElement extends Component {
}
// Add error message
- if (this.props.hasError
+ if (this.props.errorMessage
|| (this.props.required && this.props.value === '')
) {
errorMessage = {this.props.errorMessage};
@@ -660,7 +660,6 @@ SelectElement.propTypes = {
required: PropTypes.bool,
emptyOption: PropTypes.bool,
autoSelect: PropTypes.bool,
- hasError: PropTypes.bool,
errorMessage: PropTypes.string,
onUserInput: PropTypes.func,
noMargins: PropTypes.bool,
@@ -680,8 +679,7 @@ SelectElement.defaultProps = {
sortByValue: true,
emptyOption: true,
autoSelect: true,
- hasError: false,
- errorMessage: 'The field is required!',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -978,11 +976,10 @@ TagsElement.defaultProps = {
required: false,
disabled: false,
emptyOption: true,
- hasError: false,
allowDupl: false,
useSearch: false,
strictSearch: false, // only accept items specified in options
- errorMessage: '',
+ errorMessage: null,
pendingValKey: '',
btnLabel: 'Add Tag',
onUserInput: function() {
@@ -1213,7 +1210,7 @@ TextboxElement.defaultProps = {
autoComplete: null,
disabled: false,
required: false,
- errorMessage: '',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -1345,7 +1342,7 @@ EmailElement.defaultProps = {
autoComplete: null,
disabled: false,
required: false,
- errorMessage: '',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -1506,7 +1503,7 @@ PasswordElement.defaultProps = {
disabled: false,
required: false,
autoComplete: null,
- errorMessage: '',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -1560,7 +1557,7 @@ export class DateElement extends Component {
}
// Add error message
- if (this.props.hasError
+ if (this.props.errorMessage
|| (this.props.required && this.props.value === '')
) {
errorMessage = {this.props.errorMessage};
@@ -1640,7 +1637,6 @@ DateElement.propTypes = {
dateFormat: PropTypes.string,
disabled: PropTypes.bool,
required: PropTypes.bool,
- hasError: PropTypes.bool,
errorMessage: PropTypes.string,
onUserInput: PropTypes.func,
};
@@ -1655,8 +1651,7 @@ DateElement.defaultProps = {
dateFormat: 'YMd',
disabled: false,
required: false,
- hasError: false,
- errorMessage: 'The field is required!',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -2039,7 +2034,7 @@ export class FileElement extends Component {
};
// Add error message
- if (this.props.hasError) {
+ if (this.props.errorMessage) {
errorMessage = this.props.errorMessage;
elementClass = 'row form-group has-error';
}
@@ -2125,7 +2120,6 @@ FileElement.propTypes = {
disabled: PropTypes.bool,
required: PropTypes.bool,
allowMultiple: PropTypes.bool,
- hasError: PropTypes.bool,
errorMessage: PropTypes.string,
onUserInput: PropTypes.func,
};
@@ -2138,8 +2132,7 @@ FileElement.defaultProps = {
disabled: false,
required: false,
allowMultiple: false,
- hasError: false,
- errorMessage: 'The field is required!',
+ errorMessage: null,
onUserInput: function() {
console.warn('onUserInput() callback is not set');
},
@@ -2396,7 +2389,7 @@ CheckboxElement.defaultProps = {
id: null,
disabled: false,
required: false,
- errorMessage: '',
+ errorMessage: null,
offset: 'col-sm-offset-3',
class: 'checkbox-inline',
elementClass: 'checkbox-inline col-sm-offset-3',
@@ -2760,7 +2753,7 @@ RadioElement.defaultProps = {
disabled: false,
required: false,
vertical: false,
- errorMessage: '',
+ errorMessage: null,
elementClass: 'row form-group',
onUserInput: function() {
console.warn('onUserInput() callback is not set');
@@ -2905,7 +2898,7 @@ SliderElement.defaultProps = {
maxWidth: 'auto',
disabled: false,
required: false,
- errorMessage: '',
+ errorMessage: null,
elementClass: 'row form-group',
onUserInput: function() {
console.warn('onUserInput() callback is not set');
diff --git a/jsx/form/DateTimePartialElement.tsx b/jsx/form/DateTimePartialElement.tsx
index b103efb0b6d..2b0dc34b3aa 100644
--- a/jsx/form/DateTimePartialElement.tsx
+++ b/jsx/form/DateTimePartialElement.tsx
@@ -137,7 +137,6 @@ interface DateTimePartialElementProps {
dateFormat: string;
required?: boolean;
disabled?: boolean;
- hasError?: boolean;
errorMessage?: string;
onUserInput: (name: string, value: string) => void;
}
@@ -185,7 +184,7 @@ function DateTimePartialElement(props: DateTimePartialElementProps) {
if (props.required && value == '') {
errorMessage = This field is required;
elementClass += ' has-error';
- } else if (props.hasError) {
+ } else if (props.errorMessage) {
errorMessage = {props.errorMessage};
elementClass += ' has-error';
}
diff --git a/modules/battery_manager/jsx/batteryManagerForm.js b/modules/battery_manager/jsx/batteryManagerForm.js
index 6f0329c78b5..7e7761f4928 100644
--- a/modules/battery_manager/jsx/batteryManagerForm.js
+++ b/modules/battery_manager/jsx/batteryManagerForm.js
@@ -68,7 +68,6 @@ class BatteryManagerForm extends Component {
required={true}
value={test.testName}
errorMessage={errors.testName}
- hasError={errors.testName}
/>
{
swal('Error!', error, 'error');
- this.setState({error: true, success: false, emptyOption: true});
+ this.setState({error, success: false, emptyOption: true});
});
}
@@ -95,8 +95,7 @@ class FixConflictForm extends Component {
onUserInput={this.resolveConflict}
options={this.props.options}
emptyOption={emptyOption}
- hasError={error}
- errorMessage={''}
+ errorMessage={error}
noMargins={true}
/>
|
diff --git a/modules/data_release/jsx/addPermissionForm.js b/modules/data_release/jsx/addPermissionForm.js
index 5dd8a471a96..859541e82ec 100644
--- a/modules/data_release/jsx/addPermissionForm.js
+++ b/modules/data_release/jsx/addPermissionForm.js
@@ -25,7 +25,6 @@ class AddPermissionForm extends Component {
data: {},
fieldOptions: {},
formData: {},
- hasError: {},
errorMessage: {},
isLoaded: false,
loadedData: 0,
@@ -95,7 +94,6 @@ class AddPermissionForm extends Component {
options={this.state.fieldOptions.users}
onUserInput={this.setFormData}
ref='userid'
- hasError={this.state.hasError.Username}
errorMessage={this.state.errorMessage.Username}
required={true}
value={this.state.formData.userid}
@@ -107,7 +105,6 @@ class AddPermissionForm extends Component {
options={this.state.fieldOptions.filenames}
onUserInput={this.setFormData}
ref='data_release_id'
- hasError={this.state.hasError.Filename}
errorMessage={this.state.errorMessage.Filename}
required={false}
value={this.state.formData.data_release_id}
@@ -120,7 +117,6 @@ class AddPermissionForm extends Component {
options={this.state.fieldOptions.versions}
onUserInput={this.setFormData}
ref='data_release_version'
- hasError={this.state.hasError.Version}
errorMessage={this.state.errorMessage.Version}
required={false}
value={this.state.formData.data_release_version}
@@ -208,16 +204,9 @@ class AddPermissionForm extends Component {
Version: undefined,
};
- let hasError = {
- Username: false,
- Filename: false,
- Version: false,
- };
-
// make sure a user was selected
if (!formData.userid) {
errorMessage.Username = 'You must select a user!';
- hasError.Username = true;
isValid = false;
}
@@ -227,12 +216,10 @@ class AddPermissionForm extends Component {
let msg = 'You must select a file OR a version to grant permission on!';
errorMessage.Filename = msg;
errorMessage.Version = msg;
- hasError.Filename = true;
- hasError.Version = true;
isValid = false;
}
- this.setState({errorMessage, hasError});
+ this.setState({errorMessage});
return isValid;
}
}
diff --git a/modules/data_release/jsx/managePermissionsForm.js b/modules/data_release/jsx/managePermissionsForm.js
index 17bc3416e5d..d98602a8856 100644
--- a/modules/data_release/jsx/managePermissionsForm.js
+++ b/modules/data_release/jsx/managePermissionsForm.js
@@ -26,7 +26,6 @@ class ManagePermissionsForm extends Component {
this.state = {
data: {},
originalData: {},
- hasError: {},
errorMessage: {},
isLoaded: false,
};
diff --git a/modules/data_release/jsx/uploadFileForm.js b/modules/data_release/jsx/uploadFileForm.js
index 0be347b2f5e..7abe8610f23 100644
--- a/modules/data_release/jsx/uploadFileForm.js
+++ b/modules/data_release/jsx/uploadFileForm.js
@@ -29,7 +29,6 @@ class UploadFileForm extends Component {
formData: {},
uploadResult: null,
errorMessage: {},
- hasError: {},
isLoaded: false,
uploadProgress: -1,
};
@@ -127,22 +126,15 @@ class UploadFileForm extends Component {
Filesize: undefined,
};
- let hasError = {
- Filename: undefined,
- Filesize: undefined,
- };
-
if (!formData.file) {
errorMessage.Filename = 'You must select a file to upload';
- hasError.Filename = true;
- this.setState({errorMessage, hasError});
+ this.setState({errorMessage});
return;
}
if (!formData.project) {
errorMessage.Project = 'You must select a project';
- hasError.Project = true;
- this.setState({errorMessage, hasError});
+ this.setState({errorMessage});
return;
}
@@ -154,14 +146,13 @@ class UploadFileForm extends Component {
+ maxSizeAllowed
+ ')';
errorMessage['Filesize'] = msg;
- hasError['Filesize'] = true;
swal.fire({
title: 'Error',
text: msg,
type: 'error',
showCancelButton: true,
});
- this.setState({errorMessage, hasError});
+ this.setState({errorMessage});
return;
}
diff --git a/modules/document_repository/jsx/categoryForm.js b/modules/document_repository/jsx/categoryForm.js
index 446f8bce937..f3c7199b483 100644
--- a/modules/document_repository/jsx/categoryForm.js
+++ b/modules/document_repository/jsx/categoryForm.js
@@ -107,7 +107,6 @@ class DocCategoryForm extends React.Component {
options={this.state.data.fieldOptions.fileCategories}
onUserInput={this.setFormData}
disabled={disabled}
- hasError={false}
value={this.state.formData.parentId}
/>
{deleteButton}
@@ -106,9 +105,11 @@ class DeleteDocCategoryForm extends React.Component {
);
}
- /** *******************************************************************************
+ /**
+ * *******************************************************************************
* ****** Helper methods *******
- *********************************************************************************/
+ ********************************************************************************
+ */
/**
diff --git a/modules/document_repository/jsx/editCategoryForm.js b/modules/document_repository/jsx/editCategoryForm.js
index c49af8152d6..e3818370d88 100644
--- a/modules/document_repository/jsx/editCategoryForm.js
+++ b/modules/document_repository/jsx/editCategoryForm.js
@@ -82,13 +82,13 @@ class EditDocCategoryForm extends React.Component {
updateButton = ;
}
- let errorSameParent = false;
+ let errorSameParent = null;
if (
this.state.formData.categoryID==this.state.formData.newParentID
&& this.state.formData.categoryID!=null
) {
- errorSameParent = true;
+ errorSameParent = 'Cannot be equal to itself';
}
return (
@@ -106,7 +106,6 @@ class EditDocCategoryForm extends React.Component {
onUserInput={this.setFormData}
required={true}
disabled={disabled}
- hasError={false}
value={this.state.formData.categoryID}
/>
{updateButton}
@@ -135,9 +133,11 @@ class EditDocCategoryForm extends React.Component {
);
}
- /** *******************************************************************************
+ /**
+ * *******************************************************************************
* ****** Helper methods *******
- *********************************************************************************/
+ ********************************************************************************
+ */
/**
* Handle form submission
diff --git a/modules/document_repository/jsx/editForm.js b/modules/document_repository/jsx/editForm.js
index d8c0598fa28..821bbd07735 100644
--- a/modules/document_repository/jsx/editForm.js
+++ b/modules/document_repository/jsx/editForm.js
@@ -101,7 +101,6 @@ class DocEditForm extends React.Component {
label="Category"
options={this.state.data.categories}
onUserInput={this.setFormData}
- hasError={false}
required={true}
disabled={categoryDisabled}
value={this.state.docData.category}
diff --git a/modules/document_repository/jsx/uploadForm.js b/modules/document_repository/jsx/uploadForm.js
index d1d5604a72c..e729ec71979 100644
--- a/modules/document_repository/jsx/uploadForm.js
+++ b/modules/document_repository/jsx/uploadForm.js
@@ -109,7 +109,6 @@ class DocUploadForm extends Component {
label="Category"
options={this.state.data.fieldOptions.fileCategories}
onUserInput={this.setFormData}
- hasError={false}
required={true}
value={this.state.formData.category}
/>
diff --git a/modules/electrophysiology_uploader/jsx/UploadForm.js b/modules/electrophysiology_uploader/jsx/UploadForm.js
index 46e67b4d176..0eee7d30e57 100644
--- a/modules/electrophysiology_uploader/jsx/UploadForm.js
+++ b/modules/electrophysiology_uploader/jsx/UploadForm.js
@@ -18,7 +18,6 @@ import {
*/
export default function UploadForm(props) {
const [formData, setFormData] = useState({});
- const [hasError, setHasError] = useState({});
const [errorMessage, setErrorMessage] = useState({});
const [uploadProgress, setuploadProgress] = useState(-1);
@@ -28,7 +27,6 @@ export default function UploadForm(props) {
const resetForm = () => {
setFormData({});
setErrorMessage({});
- setHasError({});
setuploadProgress(-1);
};
@@ -84,13 +82,6 @@ export default function UploadForm(props) {
visit: null,
});
- setHasError({
- eegFile: true,
- candID: false,
- pscid: false,
- visit: false,
- });
-
return;
}
@@ -110,13 +101,6 @@ export default function UploadForm(props) {
visit: null,
});
- setHasError({
- eegFile: true,
- candID: false,
- pscid: false,
- visit: false,
- });
-
return;
}
@@ -147,7 +131,7 @@ export default function UploadForm(props) {
xhr.addEventListener('load', () => {
if (xhr.status < 400) {
// Upon successful upload:
- // - Resets errorMessage and hasError so no errors are displayed on form
+ // - Resets errorMessage so no errors are displayed on form
// - Displays pop up window with success message
// - Returns to Browse tab
setErrorMessage({
@@ -156,12 +140,6 @@ export default function UploadForm(props) {
pscid: null,
visit: null,
});
- setHasError({
- eegFile: false,
- candID: false,
- pscid: false,
- visit: false,
- });
let text = '';
if (props.autoLaunch === 'true' || props.autoLaunch === '1') {
@@ -195,7 +173,7 @@ export default function UploadForm(props) {
const processError = (xhr) => {
// Upon errors in upload:
// - Displays pop up window with submission error message
- // - Updates errorMessage and hasError so relevant errors are displayed on form
+ // - Updates errorMessage so relevant errors are displayed on form
// - Returns to Upload tab
console.error(xhr.status + ': ' + xhr.statusText);
@@ -239,7 +217,6 @@ export default function UploadForm(props) {
label='File to Upload'
onUserInput={onFormChange}
required={true}
- hasError={hasError.eegFile}
errorMessage={errorMessage.eegFile}
value={formData.eegFile}
/>
@@ -248,7 +225,6 @@ export default function UploadForm(props) {
label='CandID'
disabled={true}
required={false}
- hasError={hasError.candID}
errorMessage={errorMessage.candID}
value={formData.candID}
/>
@@ -257,7 +233,6 @@ export default function UploadForm(props) {
label='PSCID'
disabled={true}
required={false}
- hasError={hasError.pscid}
errorMessage={errorMessage.pscid}
value={formData.pscid}
/>
@@ -266,7 +241,6 @@ export default function UploadForm(props) {
label='Visit Label'
disabled={true}
required={false}
- hasError={hasError.visit}
errorMessage={errorMessage.visit}
value={formData.visit}
/>
diff --git a/modules/genomic_browser/jsx/tabs_content/filemanager/uploadForm.js b/modules/genomic_browser/jsx/tabs_content/filemanager/uploadForm.js
index e83ac92853a..326dbcb8b01 100644
--- a/modules/genomic_browser/jsx/tabs_content/filemanager/uploadForm.js
+++ b/modules/genomic_browser/jsx/tabs_content/filemanager/uploadForm.js
@@ -152,7 +152,6 @@ class GenomicUploadForm extends Component {
options={this.state.options.fileTypes}
onUserInput={this.setFileUploadFormData}
ref='fileType'
- hasError={false}
required={true}
value={this.state.formData.fileType}
/>
diff --git a/modules/imaging_uploader/jsx/UploadForm.js b/modules/imaging_uploader/jsx/UploadForm.js
index 7e85ada3698..162e7dc39de 100644
--- a/modules/imaging_uploader/jsx/UploadForm.js
+++ b/modules/imaging_uploader/jsx/UploadForm.js
@@ -32,7 +32,6 @@ class UploadForm extends Component {
this.state = {
formData: {},
form: form,
- hasError: {},
errorMessage: {},
uploadProgress: -1,
};
@@ -157,14 +156,7 @@ class UploadForm extends Component {
visitLabel: undefined,
};
- let hasError = {
- mriFile: true,
- candID: false,
- pSCID: false,
- visitLabel: false,
- };
-
- this.setState({errorMessage, hasError});
+ this.setState({errorMessage});
return;
}
@@ -288,18 +280,16 @@ class UploadForm extends Component {
xhr.addEventListener('load', () => {
if (xhr.status < 400) {
// Upon successful upload:
- // - Resets errorMessage and hasError so no errors are displayed on form
+ // - Resets errorMessage so no errors are displayed on form
// - Displays pop up window with success message
// - Returns to Browse tab
const errorMessage = this.state.errorMessage;
- const hasError = this.state.hasError;
for (let i in errorMessage) {
if (errorMessage.hasOwnProperty(i)) {
- errorMessage[i] = '';
- hasError[i] = false;
+ errorMessage[i] = null;
}
}
- this.setState({errorMessage: errorMessage, hasError: hasError});
+ this.setState({errorMessage: errorMessage});
let text = '';
if (this.props.imagingUploaderAutoLaunch === 'true' ||
this.props.imagingUploaderAutoLaunch === '1'
@@ -337,13 +327,12 @@ class UploadForm extends Component {
processError(xhr) {
// Upon errors in upload:
// - Displays pop up window with submission error message
- // - Updates errorMessage and hasError so relevant errors are displayed on form
+ // - Updates errorMessage so relevant errors are displayed on form
// - Returns to Upload tab
console.error(xhr.status + ': ' + xhr.statusText);
let errorMessage = Object.assign({}, this.state.errorMessage);
- const hasError = Object.assign({}, this.state.hasError);
let messageToPrint = '';
if (xhr.response) {
const resp = JSON.parse(xhr.response);
@@ -370,12 +359,11 @@ class UploadForm extends Component {
};
}
for (const [key, error] of Object.entries(errorMessage)) {
- errorMessage[key] = error.toString();
if (error.length) {
- hasError[key] = true;
+ errorMessage[key] = error.toString();
messageToPrint += error + '\n';
} else {
- hasError[key] = false;
+ errorMessage[key] = null;
}
}
swal.fire({
@@ -386,7 +374,6 @@ class UploadForm extends Component {
this.setState({
uploadProgress: -1,
errorMessage: errorMessage,
- hasError: hasError,
});
}
@@ -424,8 +411,8 @@ class UploadForm extends Component {
// Returns individual form elements
// For CandID, PSCID, and Visit Label, disabled and required
// are updated depending on Phantom Scan value
- // For all elements, hasError and errorMessage
- // are updated depending on what values are submitted
+ // For all elements, errorMessage
+ // is updated depending on what values are submitted
return (
@@ -441,7 +428,6 @@ class UploadForm extends Component {
options={this.props.form.IsPhantom.options}
onUserInput={this.onFormChange}
required={true}
- hasError={this.state.hasError.IsPhantom}
errorMessage={this.state.errorMessage.IsPhantom}
value={this.state.formData.IsPhantom}
/>
@@ -450,7 +436,6 @@ class UploadForm extends Component {
label='CandID'
disabled={true}
required={false}
- hasError={this.state.hasError.candID}
errorMessage={this.state.errorMessage.candID}
value={this.state.formData.candID}
/>
@@ -459,7 +444,6 @@ class UploadForm extends Component {
label='PSCID'
disabled={true}
required={false}
- hasError={this.state.hasError.pSCID}
errorMessage={this.state.errorMessage.pSCID}
value={this.state.formData.pSCID}
/>
@@ -468,7 +452,6 @@ class UploadForm extends Component {
label='Visit Label'
disabled={true}
required={false}
- hasError={this.state.hasError.visitLabel}
errorMessage={this.state.errorMessage.visitLabel}
value={this.state.formData.visitLabel}
/>
@@ -477,7 +460,6 @@ class UploadForm extends Component {
label='File to Upload'
onUserInput={this.onFormChange}
required={true}
- hasError={this.state.hasError.mriFile}
errorMessage={this.state.errorMessage.mriFile}
value={this.state.formData.mriFile}
/>
diff --git a/modules/issue_tracker/jsx/IssueForm.js b/modules/issue_tracker/jsx/IssueForm.js
index 231d99310be..1d03c3c0785 100644
--- a/modules/issue_tracker/jsx/IssueForm.js
+++ b/modules/issue_tracker/jsx/IssueForm.js
@@ -528,7 +528,7 @@ class IssueForm extends Component {
if (formDataToCheck[field]) {
requiredFields[field] = formDataToCheck[field];
} else if (formRefs[field]) {
- formRefs[field].props.hasError = true;
+ formRefs[field].props.errorMessage = 'This field is required';
isValidForm = false;
}
});
diff --git a/modules/issue_tracker/jsx/IssueTrackerBatchMode.js b/modules/issue_tracker/jsx/IssueTrackerBatchMode.js
index 986b1d2d39e..3b2aaeb2575 100644
--- a/modules/issue_tracker/jsx/IssueTrackerBatchMode.js
+++ b/modules/issue_tracker/jsx/IssueTrackerBatchMode.js
@@ -121,10 +121,18 @@ function IssueTrackerBatchMode({options}) {
}
// Pagination functions
+ /**
+ *
+ * @param pageNumber
+ */
function changePage(pageNumber) {
setPage((prevPage) => ({...prevPage, number: pageNumber}));
}
+ /**
+ *
+ * @param e
+ */
function updatePageRows(e) {
const newRowsPerPage = parseInt(e.target.value, 10);
setPage({number: 1, rows: newRowsPerPage});
diff --git a/modules/media/jsx/uploadForm.js b/modules/media/jsx/uploadForm.js
index bdcb72a4f53..66c5495a1c4 100644
--- a/modules/media/jsx/uploadForm.js
+++ b/modules/media/jsx/uploadForm.js
@@ -148,7 +148,6 @@ class MediaUploadForm extends Component {
options={this.state.Data.candidates}
onUserInput={this.setFormData}
ref='pscid'
- hasError={false}
required={true}
value={this.state.formData.pscid}
/>
@@ -419,7 +418,7 @@ class MediaUploadForm extends Component {
if (formData[field]) {
requiredFields[field] = formData[field];
} else if (formRefs[field]) {
- formRefs[field].props.hasError = true;
+ formRefs[field].props.errorMessage = 'This field is required.';
isValidForm = false;
}
});
From 8e83516e2be6e38a78d99dae9bb4bd4627b22792 Mon Sep 17 00:00:00 2001
From: Ayush Bhardwaj <96904351+ay-bh@users.noreply.github.com>
Date: Thu, 7 Nov 2024 21:44:54 +0530
Subject: [PATCH 24/28] [issue_tracker] Convert batch/normal mode toggle to
tabs and implement permission control (#9434)
- Replaced the view toggle button with a tabbed interface using `Tabs`
component
- Added permission check to show Batch Edit tab only for users with
`issue_tracker_developer` permission
- Implemented data refresh when switching back to Browse Issues tab
#### Link(s) to related issue(s)
* Resolves #9422, #9423
* Part of #9418
---
.../issue_tracker/jsx/issueTrackerIndex.js | 71 +++++++++++++------
1 file changed, 49 insertions(+), 22 deletions(-)
diff --git a/modules/issue_tracker/jsx/issueTrackerIndex.js b/modules/issue_tracker/jsx/issueTrackerIndex.js
index 80baf03d9c1..7557e658d63 100644
--- a/modules/issue_tracker/jsx/issueTrackerIndex.js
+++ b/modules/issue_tracker/jsx/issueTrackerIndex.js
@@ -1,6 +1,7 @@
import {createRoot} from 'react-dom/client';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
+import {Tabs, TabPane} from 'Tabs';
import Loader from 'Loader';
import FilterableDataTable from 'FilterableDataTable';
@@ -21,12 +22,12 @@ class IssueTrackerIndex extends Component {
data: {},
error: false,
isLoaded: false,
- view: 'normal', // 'normal' for FilterableDataTable, 'batch' for IssueTrackerBatchMode
+ activeTab: 'browse',
};
this.fetchData = this.fetchData.bind(this);
this.formatColumn = this.formatColumn.bind(this);
- this.toggleView = this.toggleView.bind(this);
+ this.handleTabChange = this.handleTabChange.bind(this);
}
/**
@@ -37,6 +38,21 @@ class IssueTrackerIndex extends Component {
.then(() => this.setState({isLoaded: true}));
}
+ /**
+ * Called by React when the component updates.
+ * @param {object} prevProps - Previous props
+ * @param {object} prevState - Previous state
+ */
+ componentDidUpdate(prevProps, prevState) {
+ // If the activeTab has changed to 'browse', refetch data
+ if (prevState.activeTab !== 'browse' && this.state.activeTab === 'browse') {
+ this.setState({isLoaded: false}, () => {
+ this.fetchData()
+ .then(() => this.setState({isLoaded: true}));
+ });
+ }
+ }
+
/**
* Retrieve data from the provided URL and save it in state
* Additionally add hiddenHeaders to global loris variable
@@ -55,13 +71,12 @@ class IssueTrackerIndex extends Component {
}
/**
- * Toggle between normal and batch mode
+ * Handle tab changes
+ *
+ * @param {string} newTab - The ID of the newly selected tab
*/
- toggleView() {
- this.setState((prevState) => ({
- view: prevState.view === 'normal' ? 'batch' : 'normal',
- }));
- this.fetchData(); // Fetch fresh data when toggling views
+ handleTabChange(newTab) {
+ this.setState({activeTab: newTab});
}
/**
@@ -275,18 +290,29 @@ class IssueTrackerIndex extends Component {
{label: 'New Issue', action: addIssue},
];
+ const tabList = [
+ {
+ id: 'browse',
+ label: 'Browse Issues',
+ },
+ ];
+
+ // Only display the Batch mode tab if user has the required permission
+ if (this.props.hasPermission('issue_tracker_developer')) {
+ tabList.push({
+ id: 'batch',
+ label: 'Batch Edit',
+ });
+ }
+
return (
-
-
-
-
- {this.state.view === 'normal' ? (
+
+
- ) : (
+
+
- )}
-
+
+
);
}
}
From 8451f3b212c9005a6ba30e41f1396c434c3014a4 Mon Sep 17 00:00:00 2001
From: Henri Rabalais
Date: Thu, 7 Nov 2024 19:14:03 +0100
Subject: [PATCH 25/28] [JSX] Add ProgressBar to FilterableDataTable for
Progressive Loading UI (#9401)
This incorporates the ProgressBar into the the Filterable Datatable
so that a progress can be displayed while data is being fetched.
---
jsx/FilterableDataTable.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/jsx/FilterableDataTable.js b/jsx/FilterableDataTable.js
index 2f64dd1f5ee..086bab73b58 100644
--- a/jsx/FilterableDataTable.js
+++ b/jsx/FilterableDataTable.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import Panel from 'jsx/Panel';
import DataTable from 'jsx/DataTable';
import Filter from 'jsx/Filter';
+import ProgressBar from 'jsx/ProgressBar';
/**
* FilterableDataTable component.
@@ -149,7 +150,10 @@ class FilterableDataTable extends Component {
/>
);
- const dataTable = (
+ const {progress} = this.props;
+ const dataTable = !isNaN(progress) && progress < 100 ? (
+
+ ) : (
Date: Thu, 7 Nov 2024 13:22:23 -0500
Subject: [PATCH 26/28] Add JSON HTTP responses for "accepted" and "see other"
(#9456)
Add JSON responses for HTTP codes
[Accepted](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202)
and [See
other](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303).
---
src/Http/Response/JSON/Accepted.php | 40 +++++++++++++++++++++++++
src/Http/Response/JSON/SeeOther.php | 45 +++++++++++++++++++++++++++++
2 files changed, 85 insertions(+)
create mode 100644 src/Http/Response/JSON/Accepted.php
create mode 100644 src/Http/Response/JSON/SeeOther.php
diff --git a/src/Http/Response/JSON/Accepted.php b/src/Http/Response/JSON/Accepted.php
new file mode 100644
index 00000000000..e2522778b30
--- /dev/null
+++ b/src/Http/Response/JSON/Accepted.php
@@ -0,0 +1,40 @@
+
+ * @author Loris Team
+ * @link https://www.github.com/aces/Loris/
+ *
+ * @see https://www.php-fig.org/psr/psr-7/
+ * @see https://www.php-fig.org/psr/psr-15/
+ */
+namespace LORIS\Http\Response\JSON;
+
+use \LORIS\Http\Response\JsonResponse;
+
+/**
+ * A LORIS Http Response is an implementation of the PSR15 ResponseInterface
+ * to use in LORIS specific for 202 Accepted.
+ *
+ * @category PSR15
+ * @package Http
+ * @author Loris Team
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+class Accepted extends JsonResponse
+{
+ /**
+ * Create a Json response specific to 202 Accepted
+ *
+ * @param array|null $body The error message
+ */
+ public function __construct(?array $body)
+ {
+ parent::__construct($body, 202);
+ }
+}
diff --git a/src/Http/Response/JSON/SeeOther.php b/src/Http/Response/JSON/SeeOther.php
new file mode 100644
index 00000000000..ec25a323e87
--- /dev/null
+++ b/src/Http/Response/JSON/SeeOther.php
@@ -0,0 +1,45 @@
+
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ *
+ * @see https://www.php-fig.org/psr/psr-7/
+ * @see https://www.php-fig.org/psr/psr-15/
+ */
+namespace LORIS\Http\Response\JSON;
+
+use \LORIS\Http\Response\JsonResponse;
+use \Psr\Http\Message\UriInterface;
+
+/**
+ * A LORIS Http Response is an implementation of the PSR15 ResponseInterface
+ * to use in LORIS specific for 303 See Other.
+ *
+ * @category PSR15
+ * @package Http
+ * @author Loris Team
+ * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
+ * @link https://www.github.com/aces/Loris/
+ */
+class SeeOther extends JsonResponse
+{
+ /**
+ * Create a Json response specific to 303 See Other.
+ *
+ * @param UriInterface $location The endpoint etag
+ */
+ public function __construct(UriInterface $location)
+ {
+ $headers = array('Location' => $location->__toString());
+ parent::__construct(null, 303, $headers);
+ }
+}
From 0b1cf9ba8fa2d9050478fd95587fbe0a2b6513ca Mon Sep 17 00:00:00 2001
From: Maxime Mulder
Date: Thu, 7 Nov 2024 14:34:42 -0500
Subject: [PATCH 27/28] Factorize JSX input label (#9426)
- Factorize input labels in `jsx/Form.js`
- More idiomatic React code in `jsx/form/DateTimePartialElement.tsx`
- Do not convert `disabled`, `required` and `multiple` to strings as
input expect booleans
([doc](https://react.dev/reference/react-dom/components/input)).
Note that I did not factorize checkboxes for radio buttons and
checkboxes as they use a different layout.
---
jsx/Form.js | 381 ++++++++--------------------
jsx/form/DateTimePartialElement.tsx | 87 +++----
jsx/form/InputLabel.tsx | 23 ++
3 files changed, 170 insertions(+), 321 deletions(-)
create mode 100644 jsx/form/InputLabel.tsx
diff --git a/jsx/Form.js b/jsx/Form.js
index 08efe2eacde..9b9bf0bc6c6 100644
--- a/jsx/Form.js
+++ b/jsx/Form.js
@@ -7,6 +7,7 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
+import InputLabel from 'jsx/form/InputLabel';
/**
* Form Component.
@@ -329,20 +330,12 @@ export class SearchableDropdown extends Component {
* @return {JSX} - React markup for the component
*/
render() {
- let required = this.props.required ? 'required' : null;
- let disabled = this.props.disabled ? 'disabled' : null;
let sortByValue = this.props.sortByValue;
let options = this.props.options;
let strictMessage = 'Entry must be included in provided list of options.';
let errorMessage = null;
- let requiredHTML = null;
let elementClass = 'row form-group';
- // Add required asterix
- if (required) {
- requiredHTML = *;
- }
-
// Add error message
if (this.props.errorMessage) {
errorMessage = {this.props.errorMessage};
@@ -391,10 +384,7 @@ export class SearchableDropdown extends Component {
return (
-
+