From 7f1d0449e2a384f85db8d529abb1e411b80f993d Mon Sep 17 00:00:00 2001 From: Spamhurts Date: Fri, 5 Mar 2021 16:06:59 -0600 Subject: [PATCH 1/9] Added EventsWidget HTML page --- snprc_ehr/resources/views/EventsWidget.html | 80 +++++++++++++++++++ .../resources/views/EventsWidgit.view.xml | 8 ++ 2 files changed, 88 insertions(+) create mode 100644 snprc_ehr/resources/views/EventsWidget.html create mode 100644 snprc_ehr/resources/views/EventsWidgit.view.xml diff --git a/snprc_ehr/resources/views/EventsWidget.html b/snprc_ehr/resources/views/EventsWidget.html new file mode 100644 index 000000000..7b04d948b --- /dev/null +++ b/snprc_ehr/resources/views/EventsWidget.html @@ -0,0 +1,80 @@ + + + +
+

LabKey Procedure Entry Widget

+
+ + +
+ +
+
+ + + diff --git a/snprc_ehr/resources/views/EventsWidgit.view.xml b/snprc_ehr/resources/views/EventsWidgit.view.xml new file mode 100644 index 000000000..de1bb8f75 --- /dev/null +++ b/snprc_ehr/resources/views/EventsWidgit.view.xml @@ -0,0 +1,8 @@ + + + + + + + + From dd3fb2d6515c1e9c955f8fba74fb317bdc1f7828 Mon Sep 17 00:00:00 2001 From: Spamhurts Date: Thu, 11 Mar 2021 11:45:57 -0600 Subject: [PATCH 2/9] 1. Corrected adult age by gender in PotentialDams.sql 2. Closed AcqCodes that haven't been used in the past five years (in tsv file) 3. Added missing acquisition codes to tsv file 4. Filtered acq code 97 from fetchAcquisitionTypes API 5. Removed the join on the v_demographics view from arrivals and departures ETL source queries - if animal is removed from the colony before the ETL runs, then the deletion will be missed 6. Started refactoring retrieval of dams and sires to use a birthdate parameter --- snprc_ehr/resources/data/acquisitionType.tsv | 65 ++++++++++--------- .../ehr_lookups/AcquisitionTypesLookup.sql | 3 +- .../resources/queries/study/PotentialDams.sql | 2 +- .../create_v_delete_arrival.sql | 10 +-- .../create_v_delete_departure.sql | 8 +-- .../client/NewAnimalPage/NewAnimalPage.jsx | 32 +++++++++ .../api/fetchAcquisitionTypes.js | 4 +- .../components/DemographicsPanel.jsx | 9 ++- 8 files changed, 87 insertions(+), 46 deletions(-) diff --git a/snprc_ehr/resources/data/acquisitionType.tsv b/snprc_ehr/resources/data/acquisitionType.tsv index f366e4c69..e622bad4c 100644 --- a/snprc_ehr/resources/data/acquisitionType.tsv +++ b/snprc_ehr/resources/data/acquisitionType.tsv @@ -1,33 +1,34 @@ title value category description sort_order date_disabled - 0 Acquisition Unknown 0 - 1 Birth Colony-born, Vaginal delivery (at TBRI) 1 - 2 Birth Colony-born, Cesarean delivery (at TBRI) 2 - 3 Birth Stillbirth/Abortion 3 - 4 Acquisition Trapped by TBRI 4 - 5 Acquisition Received by trade 5 - 6 Birth Surgical Removal of dead fetus 6 - 7 Acquisition New Purchase 7 - 8 Acquisition Gift (from outside source) 8 - 9 Acquisition Return of TBRI Loan 9 - 10 Acquisition Outside Loan to TBRI 10 - 11 Acquisition Re-Purchase 11 - 12 Acquisition Admission to TBRI for treatment 12 - 13 Acquisition Admission to TBRI for boarding 13 - 14 Acquisition Purchase, wild-born, known provenance 14 - 15 Acquisition Purchase, wild-born, unknown provenance 15 - 16 Acquisition Purchase, colony-born, other colony 16 - 17 Acquisition Gift, wild-born 17 - 18 Acquisition Gift, colony-born 18 - 19 Acquisition Loan, wild-born 19 - 20 Acquisition Loan, colony-born 20 - 21 Acquisition Returned to TBRI (inadequate model) 21 - 22 Birth Colony-born, other colony, by Cesarean Section (dam resides or intending to be returned to SFBR) 22 - 23 Birth Colony-born, other colony, by Vaginal delivery (dam resides or intending to be returned to SFBR) 23 - 24 Acquisition Admit to TBRI for quarantine period (tether, surgery...prior to release) 24 - 25 Acquisition NON TxBiomed data acquisition 25 - 26 Acquisition Research related (return to TBRI) 26 - 27 Acquisition Reacquired to complete medical history 27 - 28 Birth euthanized in utero, colony management 28 - 29 Acquisition UTHS - Animal returned from UT Health Sciences Center 29 - 50 Acquisition Re-IDed (DBA addition, not physical addition to colony) 50 - 97 Acquisition Parental data only, animal never at TBRI 97 + 0 Acquisition Unknown 0 03-10-2021 + 1 Birth Colony-born, Vaginal delivery (at TBRI) 1 + 2 Birth Colony-born, Cesarean delivery (at TBRI) 2 + 3 Birth Stillbirth/Abortion 3 + 4 Acquisition Trapped by TBRI 4 03-10-2021 + 5 Acquisition Received by trade 5 + 6 Birth Surgical Removal of dead fetus 6 + 7 Acquisition New Purchase 7 + 8 Acquisition Gift (from outside source) 8 + 9 Acquisition Return of TBRI Loan 9 03-10-2021 + 10 Acquisition Outside Loan to TBRI 10 03-10-2021 + 11 Acquisition Re-Purchase 11 03-10-2021 + 12 Acquisition Admission to TBRI for treatment 12 + 13 Acquisition Admission to TBRI for boarding 13 03-10-2021 + 14 Acquisition Purchase, wild-born, known provenance 14 03-10-2021 + 15 Acquisition Purchase, wild-born, unknown provenance 15 03-10-2021 + 16 Acquisition Purchase, colony-born, other colony 16 + 17 Acquisition Gift, wild-born 17 03-10-2021 + 18 Acquisition Gift, colony-born 18 + 19 Acquisition Loan, wild-born 19 03-10-2021 + 20 Acquisition Loan, colony-born 20 03-10-2021 + 21 Acquisition Returned to TBRI (inadequate model) 21 03-10-2021 + 22 Birth Colony-born, other colony, by Cesarean Section (dam resides or intending to be returned to SFBR) 22 + 23 Birth Colony-born, other colony, by Vaginal delivery (dam resides or intending to be returned to SFBR) 23 + 24 Acquisition Admit to TBRI for quarantine period (tether, surgery...prior to release) 24 03-10-2021 + 25 Acquisition NON TxBiomed data acquisition 25 + 26 Acquisition Research related (return to TBRI) 26 03-10-2021 + 27 Acquisition Reacquired to complete medical history 27 03-10-2021 + 28 Birth euthanized in utero, colony management 28 + 29 Acquisition UTHS - Animal returned from UT Health Sciences Center 29 03-10-2021 + 30 Acquisition Admission to TBRI for research treatment 30 + 50 Acquisition Re-IDed (DBA addition, not physical addition to colony) 50 03-10-2021 + 97 Acquisition Parental data only, animal never at TBRI 97 diff --git a/snprc_ehr/resources/queries/ehr_lookups/AcquisitionTypesLookup.sql b/snprc_ehr/resources/queries/ehr_lookups/AcquisitionTypesLookup.sql index 8cae04aaf..1469b0586 100644 --- a/snprc_ehr/resources/queries/ehr_lookups/AcquisitionTypesLookup.sql +++ b/snprc_ehr/resources/queries/ehr_lookups/AcquisitionTypesLookup.sql @@ -2,4 +2,5 @@ SELECT a.value as AcqCode, a.category as Category, rtrim(a.value) + ' - ' + a.description as DisplayValue, a.sort_order as SortOrder -from ehr_lookups.AcquisitionType as a \ No newline at end of file +from ehr_lookups.AcquisitionType as a +where a.date_disabled is null diff --git a/snprc_ehr/resources/queries/study/PotentialDams.sql b/snprc_ehr/resources/queries/study/PotentialDams.sql index e53f9259b..4c9dec6ba 100644 --- a/snprc_ehr/resources/queries/study/PotentialDams.sql +++ b/snprc_ehr/resources/queries/study/PotentialDams.sql @@ -11,7 +11,7 @@ FROM study.demographics AS d INNER JOIN ( SELECT "min" AS minAdultAge, species, gender, label FROM ehr_lookups.ageclass AS ac1 -) as x ON d.species.arc_species_code = x.species and x.label = 'Adult' AND (x.gender = 'M' OR x.gender is NULL) +) as x ON d.species.arc_species_code = x.species and x.label = 'Adult' AND (x.gender = 'F' OR x.gender is NULL) -- hard code until the gestation data is available in LK - use 185 if we don't have a value INNER JOIN ( diff --git a/snprc_ehr/resources/source_queries/create_v_delete_arrival.sql b/snprc_ehr/resources/source_queries/create_v_delete_arrival.sql index 302f78daf..741187660 100644 --- a/snprc_ehr/resources/source_queries/create_v_delete_arrival.sql +++ b/snprc_ehr/resources/source_queries/create_v_delete_arrival.sql @@ -33,21 +33,21 @@ ALTER VIEW [labkey_etl].[V_DELETE_ARRIVAL] AS -- Create date: 4/3/2015 -- Description: Selects the ETL records for LabKey study.arrival dataset which need to be deleted -- Changes: --- +-- 3/10/2021 Removed the join on the v_demographics view - if animal is removed from the colony +-- before the arrivals ETL runs, then the arrivals deletion will be missed -- -- ========================================================================================== -SELECT +SELECT ad.object_id, ad.audit_date_tm FROM audit.audit_acq_disp AS ad --- select primates only from the TxBiomed colony -INNER JOIN Labkey_etl.V_DEMOGRAPHICS AS d ON d.id = ad.id + WHERE ad.audit_action = 'D' AND ad.object_id IS NOT NULL GO -GRANT SELECT ON Labkey_etl.v_delete_arrival TO z_labkey +GRANT SELECT ON Labkey_etl.v_delete_arrival TO z_labkey GRANT SELECT ON audit.audit_acq_disp TO z_labkey GO diff --git a/snprc_ehr/resources/source_queries/create_v_delete_departure.sql b/snprc_ehr/resources/source_queries/create_v_delete_departure.sql index b245079e0..0df1daf4a 100644 --- a/snprc_ehr/resources/source_queries/create_v_delete_departure.sql +++ b/snprc_ehr/resources/source_queries/create_v_delete_departure.sql @@ -33,20 +33,20 @@ ALTER VIEW [labkey_etl].[V_DELETE_DEPARTURE] AS -- Create date: 6/26/2015 -- Description: Selects the ETL records for LabKey study.departure dataset for deletes -- Changes: --- +-- 3/10/2021 Removed the join on the v_demographics view - if animal is removed from the colony +-- before the departures ETL runs, then the departure deletion will be missed -- -- ========================================================================================== SELECT ad.object_id, ad.audit_date_tm FROM audit.audit_acq_disp AS ad --- select primates only from the TxBiomed colony -INNER JOIN Labkey_etl.V_DEMOGRAPHICS AS d ON d.id = ad.id + WHERE ad.audit_action = 'D' AND ad.object_id IS NOT NULL GO -GRANT SELECT ON Labkey_etl.v_delete_departure TO z_labkey +GRANT SELECT ON Labkey_etl.v_delete_departure TO z_labkey GRANT SELECT ON audit.audit_acq_disp TO z_labkey GO diff --git a/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx b/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx index 3f88e9a92..a425895c3 100644 --- a/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx @@ -453,6 +453,37 @@ export default class NewAnimalPage extends React.Component { ) }; +reloadDamsAndSires = (selectedSpecies, birthdate) => { + const lists = {} + + async function loadListsAW(species, birthdate ) { + if (this.state.selectedOption == 'Birth') { + lists.potentialDamList = await fetchPotentialDams(species, birthdate) + lists.potentialSireList = await fetchPotentialSires(species, birthdate) + } + else { + lists.potentialDamList = await fetchPotentialDams(species, birthdate) + lists.potentialSireList = await fetchPotentialSires(species, birthdate) + } + } + + loadListsAW(selectedSpecies.arcSpeciesCode, birthdate.date) + .then(() => { + this.setState(prevState => ({ + ...prevState, + potentialDamList: lists.potentialDamList, + potentialSireList: lists.potentialSireList + })) + }) + .catch(error => { + console.log(`Error in reloadDamsSires: ${error}`) + this.setState(prevState => ({ + ...prevState, + errorMessage: error.message, + })) + }) +}; + render() { // allow debug mode to be triggered for running test suite this.debug = this.props.debug !== undefined ? this.props.debug : constants.debug @@ -533,6 +564,7 @@ export default class NewAnimalPage extends React.Component { disabled={ this.disablePanels() } handleDataChange={ this.handleDataChange } newAnimalData={ this.state.newAnimalData } + selectedOption={ this.state.selectedOption } potentialDamList={ this.state.potentialDamList } diff --git a/snprc_ehr/src/client/NewAnimalPage/api/fetchAcquisitionTypes.js b/snprc_ehr/src/client/NewAnimalPage/api/fetchAcquisitionTypes.js index 59c55125f..6ed6a90de 100644 --- a/snprc_ehr/src/client/NewAnimalPage/api/fetchAcquisitionTypes.js +++ b/snprc_ehr/src/client/NewAnimalPage/api/fetchAcquisitionTypes.js @@ -19,13 +19,13 @@ const fetchAcquisitionTypes = type => { columns: ['AcqCode', 'DisplayValue', 'Category', 'SortOrder'], sort: 'SortOrder', filterArray: [ - Filter.create('Category', type, Filter.Types.EQUAL) + Filter.create('Category', type, Filter.Types.EQUAL), + Filter.create('AcqCode', 97, Filter.Types.NOT_EQUAL) // 97 is only used administatively ] }).then(({ rows }) => { resolve(parse(rows)) }).catch(error => { reject(error) - console.log('error', error) }) }) } diff --git a/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx b/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx index e1c99aea1..483739579 100644 --- a/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx @@ -13,12 +13,18 @@ export default class DemographicsPanel extends React.Component { } componentDidMount = () => { + const { birthDate, acqDate } = this.props.newAnimalData + const selectedOption = this.props.selectedOption this.setState(() => ( { - errorMessage: isBirthdateValid(this.props.newAnimalData.birthDate.date, this.props.newAnimalData.acqDate.date) ? undefined : this.dateErrorMessageText + errorMessage: isBirthdateValid(birthDate.date, acqDate.date) ? undefined : this.dateErrorMessageText } )) this.props.preventNext() + // if this.props.selectedOption = 'Acquisition' && isBirthdateValid(birthDate.date, acqDate.date) { + // load dams and sires with birthdate option + //} + } handleBdStatusChange = option => { @@ -52,6 +58,7 @@ export default class DemographicsPanel extends React.Component { render() { const { gender, dam, sire, birthDate, bdStatus } = this.props.newAnimalData + const selectedOption = this.props.selectedOption return ( <>
From 563eebfc44e240451e19d9a320f0724f47ce2764 Mon Sep 17 00:00:00 2001 From: Spamhurts Date: Mon, 29 Mar 2021 10:54:39 -0500 Subject: [PATCH 3/9] 1. Refactored PotentialDams.sql and PotentialSires.sql to use selectedOptionParm parameter to account for differences in how potential sires and dams are handled depending on whether a birth or other acquisition is being recorded 2. fetchLocations.js API updated to filter based on acquisiton codes for offsite acquisitions 3. fetchPotentialDams.js and fetchPotentialSires.js APIs updated to handle SQL parameters 4. Changes to AccountPanel.jsx to allow non-birth acquisitions to have null accounts 5. Changes to AcquisitionPanel.js to reload location codes when acquisition type changes 6. Changes made to DemographicsPanel.js to reload dams and sires after birth date is entered 7. Added new constant offSiteAcqCodes to track acquisition of animals not being added to the colony 8. Updated request API to handle parameters 9. UpdateAnimalData.js to allow null animal accounts 10. Updated ANPRC_EHRValidator.java to permit null accounts in certain conditions 11. Updated Jest tests --- .../resources/queries/study/PotentialDams.sql | 53 ++-- .../queries/study/PotentialSires.sql | 54 ++-- .../ChipReader/tests/ChipReader.test.jsx | 1 - .../client/NewAnimalPage/NewAnimalPage.jsx | 258 +++++++++-------- .../NewAnimalPage/api/fetchLocations.js | 21 +- .../NewAnimalPage/api/fetchPotentialDams.js | 4 +- .../NewAnimalPage/api/fetchPotentialSires.js | 4 +- .../NewAnimalPage/api/updateAnimalData.js | 2 +- .../NewAnimalPage/components/AccountPanel.jsx | 68 ++--- .../components/AcquisitionPanel.jsx | 2 + .../components/DemographicsPanel.jsx | 272 +++++++++--------- .../client/NewAnimalPage/constants/index.js | 1 + .../tests/NewAnimalPage.test.jsx | 16 +- .../__snapshots__/NewAnimalPage.test.jsx.snap | 7 +- .../components/DemographicsPanel.test.jsx | 1 + .../src/client/Shared/api/__mocks__/api.js | 2 +- snprc_ehr/src/client/Shared/api/api.js | 3 +- .../services/SNPRC_EHRValidator.java | 89 +++--- 18 files changed, 464 insertions(+), 394 deletions(-) diff --git a/snprc_ehr/resources/queries/study/PotentialDams.sql b/snprc_ehr/resources/queries/study/PotentialDams.sql index 4c9dec6ba..7aa519f84 100644 --- a/snprc_ehr/resources/queries/study/PotentialDams.sql +++ b/snprc_ehr/resources/queries/study/PotentialDams.sql @@ -1,41 +1,56 @@ - +PARAMETERS +( + birthdateParm TIMESTAMP DEFAULT '01/01/1900', + selectedOptionParm VARCHAR DEFAULT 'Birth' -- Birth or Acquisition +) -- TODO: Do we want to limit selection to animals that were co-located with dam? SELECT d.id as Dam, d.species as Species, d.species.arc_species_code as ArcSpeciesCode, d.gender as Gender, d.birth as BirthDate, - d.id.age.ageInYears as Age + d.id.age.ageInYears as Age, + ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) as AgeOnConceptionDate, + x.minAdultAge as MinAdultAge, + x.maxAdultAge as MaxAdultAge, + x.ageClass FROM study.demographics AS d INNER JOIN ( - SELECT "min" AS minAdultAge, species, gender, label - FROM ehr_lookups.ageclass AS ac1 + SELECT "min" AS minAdultAge, + coalesce("max", 10) AS maxAdultAge, -- some species may be missing maxAdultAge + species, + gender, + label, + ageClass + FROM ehr_lookups.ageclass AS ac ) as x ON d.species.arc_species_code = x.species and x.label = 'Adult' AND (x.gender = 'F' OR x.gender is NULL) -- hard code until the gestation data is available in LK - use 185 if we don't have a value INNER JOIN ( - SELECT 173 as gestation, 'PC' as species + SELECT 173 as gestation, timestampadd('SQL_TSI_DAY', -173, birthdateParm ) as minConceptionDate, 'PC' as species UNION ALL - SELECT 143 as gestation, 'CJ' as species + SELECT 143 as gestation, timestampadd('SQL_TSI_DAY', -143, birthdateParm ) as minConceptionDate, 'CJ' as species UNION ALL - SELECT 160 as gestation, 'MM' as species + SELECT 160 as gestation, timestampadd('SQL_TSI_DAY', -160, birthdateParm ) as minConceptionDate, 'MM' as species UNION ALL - SELECT 155 as gestation, 'MF' as species + SELECT 155 as gestation, timestampadd('SQL_TSI_DAY', -155, birthdateParm ) as minConceptionDate, 'MF' as species UNION ALL - SELECT 223 as gestation, 'PT' as species + SELECT 223 as gestation, timestampadd('SQL_TSI_DAY', -223, birthdateParm ) as minConceptionDate, 'PT' as species UNION ALL - SELECT 22 as gestation, 'MA' as species + SELECT 22 as gestation, timestampadd('SQL_TSI_DAY', -22, birthdateParm ) as minConceptionDate, 'MA' as species UNION ALL - SELECT 185 as gestation, 'O' as species + SELECT 185 as gestation, timestampadd('SQL_TSI_DAY', -185, birthdateParm ) as minConceptionDate, 'O' as species ) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT','MA') then d.species.arc_species_code ELSE 'O' END = y.species - INNER JOIN study.acq_disp as ad on d.id = ad.id - WHERE d.gender = 'F' --- age at conception is greater or equal to minimum adult age - AND x.minAdultAge <= ROUND(CONVERT(age_in_months(d.birth, timestampadd('SQL_TSI_DAY', -y.gestation, curdate())), DOUBLE) / 12.0, 1) --- ensure animal was at txbiomed on date of conception - AND timestampadd('SQL_TSI_DAY', -y.gestation, curdate()) BETWEEN ad.acq_date AND COALESCE(ad.disp_date, curdate()) --- make sure animal was alive (at center) on conception date - AND COALESCE(d.lastDayAtCenter, curdate()) > timestampadd('SQL_TSI_DAY', -y.gestation, curdate()) +-- age at conception is greater or equal to minimum adult age and less than or equal to maximum adult age + -- LK has trouble matching parameters correctly using the code below, so minConceptionDate was added to the y result set + --AND x.minAdultAge <= ROUND(CAST(age_in_months(d.birth, timestampadd('SQL_TSI_DAY', -y.gestation, coalesce(birthdateParm, curdate()) ) ) AS DOUBLE) / 12.0, 1) + AND ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) between x.minAdultAge and x.maxAdultAge +-- ensure animal was at txbiomed on date of conception for birth type acquisitions + AND (timestampadd('SQL_TSI_DAY', -y.gestation, birthdateParm) BETWEEN ad.acq_date AND COALESCE(ad.disp_date, birthdateParm) OR selectedOptionParm = 'Acquisition') +-- make sure animal was alive (at center) on conception date for birth type acquisitions + AND (COALESCE(d.lastDayAtCenter, birthdateParm) >= timestampadd('SQL_TSI_DAY', -y.gestation, birthdateParm) OR selectedOptionParm = 'Acquisition') +-- drop off animals that were not alive on the conception date + AND d.id.age.ageInYears >= ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) diff --git a/snprc_ehr/resources/queries/study/PotentialSires.sql b/snprc_ehr/resources/queries/study/PotentialSires.sql index 4cfbcfc30..6b6c2bc6c 100644 --- a/snprc_ehr/resources/queries/study/PotentialSires.sql +++ b/snprc_ehr/resources/queries/study/PotentialSires.sql @@ -1,41 +1,53 @@ - +PARAMETERS +( + birthdateParm TIMESTAMP DEFAULT '01/01/1900', + selectedOptionParm VARCHAR DEFAULT 'Birth' -- Birth or Acquisition +) -- TODO: Do we want to limit selection to animals that were co-located with dam? SELECT d.id as Sire, d.species as Species, d.species.arc_species_code as ArcSpeciesCode, - d.gender, - d.birth, - d.id.age.ageInYears as Age + d.gender as Gender, + d.birth as BirthDate, + d.id.age.ageInYears as Age, + ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) as AgeOnConceptionDate, + x.minAdultAge as MinAdultAge FROM study.demographics AS d INNER JOIN ( - SELECT "min" AS minAdultAge, species, gender, label - FROM ehr_lookups.ageclass AS ac1 + SELECT "min" AS minAdultAge, + coalesce("max", 10) AS maxAdultAge, -- some species may be missing maxAdultAge + species, + gender, + label + FROM ehr_lookups.ageclass AS ac ) as x ON d.species.arc_species_code = x.species and x.label = 'Adult' AND (x.gender = 'M' OR x.gender is NULL) -- hard code until the gestation data is available in LK - use 185 if we don't have a value INNER JOIN ( - SELECT 173 as gestation, 'PC' as species + SELECT 173 as gestation, timestampadd('SQL_TSI_DAY', -173, birthdateParm ) as minConceptionDate, 'PC' as species UNION ALL - SELECT 143 as gestation, 'CJ' as species + SELECT 143 as gestation, timestampadd('SQL_TSI_DAY', -143, birthdateParm ) as minConceptionDate, 'CJ' as species UNION ALL - SELECT 160 as gestation, 'MM' as species + SELECT 160 as gestation, timestampadd('SQL_TSI_DAY', -160, birthdateParm ) as minConceptionDate, 'MM' as species UNION ALL - SELECT 155 as gestation, 'MF' as species + SELECT 155 as gestation, timestampadd('SQL_TSI_DAY', -155, birthdateParm ) as minConceptionDate, 'MF' as species UNION ALL - SELECT 223 as gestation, 'PT' as species + SELECT 223 as gestation, timestampadd('SQL_TSI_DAY', -223, birthdateParm ) as minConceptionDate, 'PT' as species UNION ALL - SELECT 22 as gestation, 'MA' as species + SELECT 22 as gestation, timestampadd('SQL_TSI_DAY', -22, birthdateParm ) as minConceptionDate, 'MA' as species UNION ALL - SELECT 185 as gestation, 'O' as species -) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT', 'MA') then d.species.arc_species_code ELSE 'O' END = y.species - + SELECT 185 as gestation, timestampadd('SQL_TSI_DAY', -185, birthdateParm ) as minConceptionDate, 'O' as species +) AS y ON CASE WHEN d.species.arc_species_code IN ('PC', 'CJ', 'MM', 'MF', 'PT','MA') then d.species.arc_species_code ELSE 'O' END = y.species INNER JOIN study.acq_disp as ad on d.id = ad.id - WHERE d.gender = 'M' -- age at conception is greater or equal to minimum adult age - AND x.minAdultAge <= ROUND(CONVERT(age_in_months(d.birth, timestampadd('SQL_TSI_DAY', -y.gestation, curdate())), DOUBLE) / 12.0, 1) --- ensure animal was at txbiomed on date of conception - AND timestampadd('SQL_TSI_DAY', -y.gestation, curdate()) BETWEEN ad.acq_date AND COALESCE(ad.disp_date, curdate()) --- make sure animal was alive (at center) on conception date - AND COALESCE(d.lastDayAtCenter, curdate()) > timestampadd('SQL_TSI_DAY', -y.gestation, curdate()) + -- LK has trouble matching parameters correctly using the code below, so minConceptionDate was added to the y result set + --AND x.minAdultAge <= ROUND(CAST(age_in_months(d.birth, timestampadd('SQL_TSI_DAY', -y.gestation, coalesce(birthdateParm, curdate()) ) ) AS DOUBLE) / 12.0, 1) + AND ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) between x.minAdultAge and x.maxAdultAge +-- ensure animal was at txbiomed on date of conception for birth type acquisitions + AND (timestampadd('SQL_TSI_DAY', -y.gestation, birthdateParm) BETWEEN ad.acq_date AND COALESCE(ad.disp_date, birthdateParm) OR selectedOptionParm = 'Acquisition') +-- make sure animal was alive (at center) on conception date for birth type acquisitions + AND (COALESCE(d.lastDayAtCenter, birthdateParm) >= timestampadd('SQL_TSI_DAY', -y.gestation, birthdateParm) OR selectedOptionParm = 'Acquisition') +-- drop off animals that were not alive on the conception date + AND d.id.age.ageInYears >= ROUND(CAST(age_in_months(d.birth, y.minConceptionDate ) AS DOUBLE) / 12.0, 1) diff --git a/snprc_ehr/src/client/ChipReader/tests/ChipReader.test.jsx b/snprc_ehr/src/client/ChipReader/tests/ChipReader.test.jsx index fbe1bce38..8602b4486 100644 --- a/snprc_ehr/src/client/ChipReader/tests/ChipReader.test.jsx +++ b/snprc_ehr/src/client/ChipReader/tests/ChipReader.test.jsx @@ -2,7 +2,6 @@ import React from 'react' import { shallow } from 'enzyme' -import Select from 'react-select' import moment from 'moment' import ChipReader from '../ChipReader' import { executeSql } from '../../Shared/api/api' diff --git a/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx b/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx index a425895c3..6a1384244 100644 --- a/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/NewAnimalPage.jsx @@ -110,9 +110,9 @@ export default class NewAnimalPage extends React.Component { const lists = {} async function loadListsAW(species) { - lists.potentialDamList = await fetchPotentialDams(species) - lists.potentialSireList = await fetchPotentialSires(species) - lists.locationList = await fetchLocations(species) + //lists.potentialDamList = await fetchPotentialDams(species) + //lists.potentialSireList = await fetchPotentialSires(species) + //lists.locationList = await fetchLocations(species) lists.colonyList = await fetchColonies(species) lists.iacucList = await fetchProtocols(species) lists.pedigreeList = await fetchPedigrees(species) @@ -122,9 +122,9 @@ export default class NewAnimalPage extends React.Component { .then(() => { this.setState(prevState => ({ ...prevState, - potentialDamList: lists.potentialDamList, - potentialSireList: lists.potentialSireList, - locationList: lists.locationList, + //potentialDamList: lists.potentialDamList, + //potentialSireList: lists.potentialSireList, + //locationList: lists.locationList, colonyList: lists.colonyList, iacucList: lists.iacucList, pedigreeList: lists.pedigreeList, @@ -276,9 +276,9 @@ export default class NewAnimalPage extends React.Component { result = !room break case 4: - result = !animalAccount + result = ( !animalAccount && !constants.offSiteAcqCodes.includes(this.state.newAnimalData.acquisitionType.value)) || !ownerInstitution - || !iacuc + || (!iacuc && !constants.offSiteAcqCodes.includes(this.state.newAnimalData.acquisitionType.value)) || !responsibleInstitution || (!colony && this.state.colonyList.length > 0) || (!pedigree && this.state.pedigreeList > 0) @@ -453,36 +453,54 @@ export default class NewAnimalPage extends React.Component { ) }; -reloadDamsAndSires = (selectedSpecies, birthdate) => { + reloadDamsAndSires = (selectedSpecies, birthdate, selectedOption) => { const lists = {} - async function loadListsAW(species, birthdate ) { - if (this.state.selectedOption == 'Birth') { - lists.potentialDamList = await fetchPotentialDams(species, birthdate) - lists.potentialSireList = await fetchPotentialSires(species, birthdate) + async function loadListsAW(species, birthdate, selectedOption) { + lists.potentialDamList = await fetchPotentialDams(species, birthdate, selectedOption) + lists.potentialSireList = await fetchPotentialSires(species, birthdate, selectedOption) + } - else { - lists.potentialDamList = await fetchPotentialDams(species, birthdate) - lists.potentialSireList = await fetchPotentialSires(species, birthdate) + + loadListsAW(selectedSpecies.arcSpeciesCode, birthdate.date, selectedOption) + .then(() => { + this.setState(prevState => ({ + ...prevState, + potentialDamList: lists.potentialDamList, + potentialSireList: lists.potentialSireList + })) + }) + .catch(error => { + console.log(`Error in reloadDamsAndSires: ${error}`) + this.setState(prevState => ({ + ...prevState, + errorMessage: error.message, + })) + }) + }; + + reloadLocations = (species, acquisitionType) => { + const lists = {} + + async function loadListsAW(species, acquisitionType) { + lists.locationList = await fetchLocations(species, acquisitionType) } - } - loadListsAW(selectedSpecies.arcSpeciesCode, birthdate.date) - .then(() => { - this.setState(prevState => ({ - ...prevState, - potentialDamList: lists.potentialDamList, - potentialSireList: lists.potentialSireList - })) - }) - .catch(error => { - console.log(`Error in reloadDamsSires: ${error}`) - this.setState(prevState => ({ - ...prevState, - errorMessage: error.message, - })) - }) -}; + loadListsAW(species, acquisitionType) + .then(() => { + this.setState(prevState => ({ + ...prevState, + locationList: lists.locationList, + })) + }) + .catch(error => { + console.log(`Error in reloadLocations: ${error}`) + this.setState(prevState => ({ + ...prevState, + errorMessage: error.message, + })) + }) + }; render() { // allow debug mode to be triggered for running test suite @@ -500,159 +518,161 @@ reloadDamsAndSires = (selectedSpecies, birthdate) => {

Species and Acquisition Type

- { this.state.currentPanel === 1 && ( + {this.state.currentPanel === 1 && (

Acquisition

- ) } + )} - { this.state.currentPanel === 2 && ( + {this.state.currentPanel === 2 && (

Demographics

- ) } + )} - { this.state.currentPanel === 3 && ( + {this.state.currentPanel === 3 && (

Location

- ) } + )} - { this.state.currentPanel === 4 && ( + {this.state.currentPanel === 4 && (

Account, Colony, and Ownership

- ) } + )} - { this.state.currentPanel === 5 && ( + {this.state.currentPanel === 5 && (

Diet

- ) } - { this.state.errorMessage && ( + )} + {this.state.errorMessage && ( { ] } /> - ) } + )}
{ this.state.currentPanel <= 1 || this.state.hasError } - onClick={ this.handlePrevious } + onClick={this.handlePrevious} previous > ← Previous Page - { this.state.currentPanel !== this.numPanels && ( + {this.state.currentPanel !== this.numPanels && ( { || this.state.preventNext } next - onClick={ this.handleNext } + onClick={this.handleNext} > Next Page → - ) } + )} Cancel - { this.state.currentPanel === this.numPanels && ( + {this.state.currentPanel === this.numPanels && ( { || this.state.preventNext } next - onClick={ this.handleSave } + onClick={this.handleSave} > Save - ) } + )}
@@ -722,8 +742,8 @@ reloadDamsAndSires = (selectedSpecies, birthdate) => {
@@ -731,29 +751,29 @@ reloadDamsAndSires = (selectedSpecies, birthdate) => {
- {/* Save Modal */ } + {/* Save Modal */} - {/* Cancel Modal */ } + {/* Cancel Modal */} - {/* Species Change Modal */ } + {/* Species Change Modal */}
diff --git a/snprc_ehr/src/client/NewAnimalPage/api/fetchLocations.js b/snprc_ehr/src/client/NewAnimalPage/api/fetchLocations.js index 1493e85a3..c28b7e1cd 100644 --- a/snprc_ehr/src/client/NewAnimalPage/api/fetchLocations.js +++ b/snprc_ehr/src/client/NewAnimalPage/api/fetchLocations.js @@ -1,16 +1,21 @@ import { executeSql } from '../../Shared/api/api' +import constants from '../constants' const parse = rows => { return rows.map(({ data }, key) => { - return { id: key, value: data.room.value, label: data.room.value, arcSpeciesCode: data.species.value, maxCages: data.maxCages.value, rowId: data.rowId.value } + return { id: key, value: data.room.value, + label: `${data.room.value} - ${data.description.value}`, + arcSpeciesCode: data.species.value, maxCages: data.maxCages.value, + rowId: data.rowId.value } }) } -const fetchLocations = species => { - const sql = `SELECT h.species AS species, +const fetchLocations = (species, acquisitionType) => { + let sql = `SELECT h.species AS species, r.room AS room, r.maxCages AS maxCages, - r.rowId AS rowId + r.rowId AS rowId, + r.building as description FROM ehr_lookups.rooms r LEFT OUTER JOIN ( SELECT DISTINCT d.species.arc_species_code as species, d2.room AS room @@ -20,9 +25,15 @@ const fetchLocations = species => { AND d2.qcstate.publicdata = true ) AS h ON r.room = h.room WHERE r.dateDisabled is NULL - AND CAST(r.room as FLOAT) BETWEEN 1.00 AND 799.99 AND (h.species = '${species}' OR h.species is NULL)` + const { offSiteAcqCodes } = constants + if ( offSiteAcqCodes.split(',').filter(code => code == acquisitionType).length > 0 ) { + sql = sql + ` AND CAST(r.room as FLOAT) >= 800.00` + } + else { + sql = sql + ` AND CAST(r.room as FLOAT) BETWEEN 1.00 AND 799.99` + } return new Promise((resolve, reject) => { if (!species || species.length !== 2) reject(new Error('Invalid species format detected')) diff --git a/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialDams.js b/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialDams.js index 869a54c21..02b8c6499 100644 --- a/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialDams.js +++ b/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialDams.js @@ -1,3 +1,4 @@ +import moment from 'moment' import { Filter } from '@labkey/api' import { request } from '../../Shared/api/api' @@ -11,9 +12,10 @@ const parse = rows => { }) } -const fetchPotentialDams = species => { +const fetchPotentialDams = (species, birthdate, selectedOption) => { return new Promise((resolve, reject) => { request({ + parameters: {'birthdateParm': birthdate, 'selectedOptionParm': selectedOption}, schemaName: 'study', queryName: 'PotentialDams', columns: ['Dam', 'ArcSpeciesCode', 'Age'], diff --git a/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialSires.js b/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialSires.js index b135f89a3..0682ff76d 100644 --- a/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialSires.js +++ b/snprc_ehr/src/client/NewAnimalPage/api/fetchPotentialSires.js @@ -1,3 +1,4 @@ +import moment from 'moment' import { Filter } from '@labkey/api' import { request } from '../../Shared/api/api' @@ -11,9 +12,10 @@ const parse = rows => { }) } -const fetchPotentialSires = species => { +const fetchPotentialSires = (species, birthdate, selectedOption) => { return new Promise((resolve, reject) => { request({ + parameters: {'birthdateParm': birthdate, 'selectedOptionParm': selectedOption}, schemaName: 'study', queryName: 'PotentialSires', columns: ['Sire', 'ArcSpeciesCode', 'Age'], diff --git a/snprc_ehr/src/client/NewAnimalPage/api/updateAnimalData.js b/snprc_ehr/src/client/NewAnimalPage/api/updateAnimalData.js index 2903881c7..3b985b3c5 100644 --- a/snprc_ehr/src/client/NewAnimalPage/api/updateAnimalData.js +++ b/snprc_ehr/src/client/NewAnimalPage/api/updateAnimalData.js @@ -8,7 +8,6 @@ const convertToJson = newAnimalData => { acqDate: newAnimalData.acqDate.date.toString(), gender: newAnimalData.gender.value, species: newAnimalData.species.value, - animalAccount: newAnimalData.animalAccount.value, ownerInstitution: newAnimalData.ownerInstitution.value, responsibleInstitution: newAnimalData.responsibleInstitution.value, room: newAnimalData.room.rowId, @@ -18,6 +17,7 @@ const convertToJson = newAnimalData => { ...(newAnimalData.iacuc && { iacuc: newAnimalData.iacuc.value }), ...(newAnimalData.colony && { colony: newAnimalData.colony.value }), ...(newAnimalData.sire && { sire: newAnimalData.sire.value }), + ...(newAnimalData.animalAccount && { animalAccount: newAnimalData.animalAccount.value }), ...(newAnimalData.dam && { dam: newAnimalData.dam.value }) } return jsonData diff --git a/snprc_ehr/src/client/NewAnimalPage/components/AccountPanel.jsx b/snprc_ehr/src/client/NewAnimalPage/components/AccountPanel.jsx index d050e7208..5b0901382 100644 --- a/snprc_ehr/src/client/NewAnimalPage/components/AccountPanel.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/components/AccountPanel.jsx @@ -1,6 +1,7 @@ import React from 'react' import Select from 'react-select' import InfoPanel from '../../Shared/components/InfoPanel' +import constants from '../constants' export default class AccountPanel extends React.Component { componentDidMount = () => { @@ -32,10 +33,11 @@ export default class AccountPanel extends React.Component { } render() { - const { animalAccount, colony, pedigree, iacuc, responsibleInstitution, ownerInstitution } = this.props.newAnimalData + const { animalAccount, colony, pedigree, iacuc, responsibleInstitution, ownerInstitution, acquisitionType } = this.props.newAnimalData return ( <> +
@@ -44,13 +46,13 @@ export default class AccountPanel extends React.Component { autoFocus className="shared-dropdown" classNamePrefix="shared-select" - defaultValue={ animalAccount } + defaultValue={animalAccount} id="account-select" isClearable - isDisabled={ this.props.disabled } - isLoading={ this.props.accountList.length === 0 } - onChange={ this.handleAccountChange } - options={ this.props.accountList } + isDisabled={this.props.disabled} + isLoading={this.props.accountList.length === 0} + onChange={this.handleAccountChange} + options={this.props.accountList} placeholder="Select Account" />
@@ -63,13 +65,13 @@ export default class AccountPanel extends React.Component {
@@ -99,13 +101,13 @@ export default class AccountPanel extends React.Component { +
+ +
+
+ +
+ )} + + { //this.props.potentialSireList.length > 0 && + (
- + -
-
- )} - - {this.props.potentialSireList.length > 0 - && ( -
-
- -
@@ -83,13 +77,13 @@ export default class AccountPanel extends React.Component {
@@ -121,12 +115,12 @@ export default class AccountPanel extends React.Component { classNamePrefix="shared-select" id="owner-select" isClearable - isDisabled={this.props.disabled} - isLoading={this.props.institutionList.length === 0} - onChange={this.handleOwnerInstitutionChange} - options={this.props.institutionList} + isDisabled={ this.props.disabled } + isLoading={ this.props.institutionList.length === 0 } + onChange={ this.handleOwnerInstitutionChange } + options={ this.props.institutionList } placeholder="Select Owner" - value={ownerInstitution} + value={ ownerInstitution } /> @@ -138,21 +132,21 @@ export default class AccountPanel extends React.Component { classNamePrefix="shared-select" id="responsible-institution-select" isClearable - isDisabled={this.props.disabled} - isLoading={this.props.institutionList.length === 0} - onChange={this.handleResponsibleInstitutionChange} - options={this.props.institutionList} + isDisabled={ this.props.disabled } + isLoading={ this.props.institutionList.length === 0 } + onChange={ this.handleResponsibleInstitutionChange } + options={ this.props.institutionList } placeholder="Select Responsible Institution" - value={responsibleInstitution} + value={ responsibleInstitution } /> 0), colName: 'Colony' }, - { propTest: (!iacuc && !constants.offSiteAcqCodes.includes(acquisitionType.value)), colName: 'IACUC' }, + { propTest: (!offSiteAcqCodes.includes(acquisitionType.value) && !iacuc), colName: 'IACUC' }, { propTest: (!pedigree && this.props.pedigreeList.length > 0), colName: 'Pedigree' }, { propTest: !ownerInstitution, colName: 'Owner' }, { propTest: !responsibleInstitution, colName: 'Responsible Institution' } diff --git a/snprc_ehr/src/client/NewAnimalPage/components/AcquisitionPanel.jsx b/snprc_ehr/src/client/NewAnimalPage/components/AcquisitionPanel.jsx index d8063e5bf..be6279033 100644 --- a/snprc_ehr/src/client/NewAnimalPage/components/AcquisitionPanel.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/components/AcquisitionPanel.jsx @@ -11,22 +11,19 @@ export default class AcquisitionPanel extends React.Component { errorMessage: undefined, infoMessage: undefined } - - componentDidMount = () => { +componentDidMount = () => { this.props.preventNext() // prevent/Allow Next button } - - handleAcquisitionChange = option => { - const species = this.props.newAnimalData.species.arcSpeciesCode +handleAcquisitionChange = option => { this.props.handleDataChange('acquisitionType', option) - this.props.reloadLocations(species, option.value) // value contains the acq code } - - handleAcquisitionDateChange = date => { +handleAcquisitionDateChange = date => { this.props.handleDataChange('acqDate', { date: moment(date) }) } - - handleNumAnimalChange = e => { +handleSourceLocationChange = sourceLocation => { + this.props.handleDataChange('sourceLocation', sourceLocation) + } +handleNumAnimalChange = e => { const numAnimals = e.target.value const errorMessage = validateNumAnimals(numAnimals) @@ -41,8 +38,7 @@ export default class AcquisitionPanel extends React.Component { } )) } - - isInteger = e => { +isInteger = e => { const i = e.key // which ? e.which : e.keyCode; const isInteger = (i >= 0 && i <= 9) if (!isInteger) { @@ -51,14 +47,13 @@ export default class AcquisitionPanel extends React.Component { return isInteger } - - handlePaste = e => { +handlePaste = e => { e.preventDefault() } - - render() { - const { acquisitionType, acqDate, species } = this.props.newAnimalData - const numAnimals = this.props.numAnimals +render() { + const { acquisitionType, acqDate, species, sourceLocation } = this.props.newAnimalData + const { numAnimals } = this.props + const { selectedOption } = this.props return ( <> @@ -98,25 +93,43 @@ export default class AcquisitionPanel extends React.Component { value={ acquisitionType || null } /> + { selectedOption && selectedOption === 'Acquisition' && ( +
+
+ + - + + +
) } @@ -124,13 +137,13 @@ export default class AcquisitionPanel extends React.Component { ) diff --git a/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx b/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx index 317322925..86b47f358 100644 --- a/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/components/DemographicsPanel.jsx @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-expressions */ import React from 'react' import Select from 'react-select' import moment from 'moment' @@ -5,17 +6,14 @@ import WrappedDatePicker from '../../Shared/components/WrappedDatePicker' import InfoPanel from '../../Shared/components/InfoPanel' import { isBirthdateValid } from '../services/validation' - export default class DemographicsPanel extends React.Component { dateErrorMessageText = 'Birthdate must occur on or before the acquisition date.' - - state = { +state = { errorMessage: undefined } - - componentDidMount = () => { +componentDidMount = () => { const { birthDate, acqDate, species } = this.props.newAnimalData - const selectedOption = this.props.selectedOption + const { selectedOption } = this.props this.setState(() => ( { errorMessage: isBirthdateValid(birthDate.date, acqDate.date) ? undefined : this.dateErrorMessageText @@ -24,43 +22,33 @@ export default class DemographicsPanel extends React.Component { this.props.reloadDamsAndSires(species, birthDate, selectedOption) this.props.preventNext() } - - handleBdStatusChange = option => { +handleBdStatusChange = option => { this.props.handleDataChange('bdStatus', option) } - - handleDamChange = option => { +handleDamChange = option => { this.props.handleDataChange('dam', option) } - - handleSireChange = option => { +handleSireChange = option => { this.props.handleDataChange('sire', option) } - - handleGenderChange = option => { +handleGenderChange = option => { this.props.handleDataChange('gender', option) } - - handleBirthDateChange = date => { - const selectedOption = this.props.selectedOption +handleBirthDateChange = date => { + const { selectedOption } = this.props const { species, acqDate } = this.props.newAnimalData const birthdate = moment(date) this.props.handleDataChange('birthDate', { date: birthdate }) - this.setState(() => ( - { + this.setState(() => ({ errorMessage: isBirthdateValid(birthdate, acqDate.date) ? undefined : this.dateErrorMessageText - } - )), () => { this.props.reloadDamsAndSires(species, birthdate, selectedOption) } - } - - handleBirthDateSelect = () => { + }), () => { this.props.reloadDamsAndSires(species, birthdate, selectedOption) }) +} +handleBirthDateSelect = () => { // do nothing } - - render() { +render() { const { gender, dam, sire, birthDate, bdStatus } = this.props.newAnimalData - const selectedOption = this.props.selectedOption return ( <>
@@ -68,15 +56,15 @@ export default class DemographicsPanel extends React.Component {
@@ -88,13 +76,13 @@ export default class DemographicsPanel extends React.Component { autoFocus className="shared-dropdown" classNamePrefix="shared-select" - defaultValue={bdStatus} + defaultValue={ bdStatus } id="bdStatus-select" isClearable - isDisabled={this.props.disabled} - isLoading={this.props.bdStatusList.length === 0} - onChange={this.handleBdStatusChange} - options={this.props.bdStatusList} + isDisabled={ this.props.disabled } + isLoading={ this.props.bdStatusList.length === 0 } + onChange={ this.handleBdStatusChange } + options={ this.props.bdStatusList } placeholder="Select Birthdate Status" />
@@ -105,19 +93,19 @@ export default class DemographicsPanel extends React.Component { - )} + ) +} - { //this.props.potentialSireList.length > 0 && + { // this.props.potentialSireList.length > 0 && (
@@ -146,22 +135,23 @@ export default class DemographicsPanel extends React.Component { + +
+)} + { numAnimals && numAnimals !== 1 + && (
- - }> + + } placement="left">
- } +)}
Demographics
{/* Demographics */} @@ -233,7 +247,7 @@ export default class SummaryPanel extends React.Component { )}
- } placement="left"> + } placement="right">
diff --git a/snprc_ehr/src/client/NewAnimalPage/constants/NewAnimalState.js b/snprc_ehr/src/client/NewAnimalPage/constants/NewAnimalState.js index 52845e42e..2f03a4664 100644 --- a/snprc_ehr/src/client/NewAnimalPage/constants/NewAnimalState.js +++ b/snprc_ehr/src/client/NewAnimalPage/constants/NewAnimalState.js @@ -27,7 +27,8 @@ const NewAnimalState = () => { cage: { value: undefined }, diet: undefined, pedigree: undefined, - iacuc: undefined + iacuc: undefined, + sourceLocation: undefined }, summaryData: [], speciesList: [], @@ -42,6 +43,7 @@ const NewAnimalState = () => { iacucList: [], pedigreeList: [], bdStatusList: [], + sourceLocationList: [], isLoading: true, hasError: false, preventNext: true, diff --git a/snprc_ehr/src/client/NewAnimalPage/constants/index.js b/snprc_ehr/src/client/NewAnimalPage/constants/index.js index 097cb993a..3af0e6659 100644 --- a/snprc_ehr/src/client/NewAnimalPage/constants/index.js +++ b/snprc_ehr/src/client/NewAnimalPage/constants/index.js @@ -2,8 +2,8 @@ export default { debug: false, numPanels: 5, defaultInstitution: { id: 0, value: 1, label: 'TxBiomed - Texas Biomedical Research Institute' }, - offSiteAcqCodes: "22,23,25,97", - hamsterWarnings: + offSiteAcqCodes: ['22', '23', '25', '97'], + hamsterWarnings: `Hamsters can be acquired in bulk; however, when added in bulk you must ensure all animals: 1) are the same sex 2) have the same birth and acquisition dates diff --git a/snprc_ehr/src/client/NewAnimalPage/services/validation.js b/snprc_ehr/src/client/NewAnimalPage/services/validation.js index 04eea8dc1..1c62f543f 100644 --- a/snprc_ehr/src/client/NewAnimalPage/services/validation.js +++ b/snprc_ehr/src/client/NewAnimalPage/services/validation.js @@ -23,5 +23,5 @@ export const validateNumAnimals = numAnimals => { hasError = numAnimals < 1 || numAnimals > 100 } - return hasError ? `Number of Animals must be between 1 and 100` : undefined -} \ No newline at end of file + return hasError ? 'Number of Animals must be between 1 and 100' : undefined +} diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/NewAnimalPage.test.jsx b/snprc_ehr/src/client/NewAnimalPage/tests/NewAnimalPage.test.jsx index b7c0d742b..5758cf90a 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/NewAnimalPage.test.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/tests/NewAnimalPage.test.jsx @@ -146,7 +146,7 @@ describe('NewAnimalPage tests', () => { // Demographics InfoPanel should be present const DemographicsPanel = wrapper.find('DemographicsPanel') expect(DemographicsPanel.dive().find('InfoPanel').exists()).toBeTruthy() - + // Birthdate state should change -- same as acquisition date const birthdateDatePicker = DemographicsPanel.dive().find('WrappedDatePicker') birthdateDatePicker.simulate('change', data.birthdate1) @@ -166,7 +166,7 @@ describe('NewAnimalPage tests', () => { await flushPromises() expect(wrapper.state().newAnimalData.gender).toEqual(data.gender) - //console.log(DemographicsPanel.dive().debug({ verbose: true })) + // console.log(DemographicsPanel.dive().debug({ verbose: true })) // Potential Dam Select const damSelect = DemographicsPanel.dive().find(Select).at(2) damSelect.simulate('change', data.potentialDam) diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/__snapshots__/NewAnimalPage.test.jsx.snap b/snprc_ehr/src/client/NewAnimalPage/tests/__snapshots__/NewAnimalPage.test.jsx.snap index 7a1eab2a2..864996d60 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/__snapshots__/NewAnimalPage.test.jsx.snap +++ b/snprc_ehr/src/client/NewAnimalPage/tests/__snapshots__/NewAnimalPage.test.jsx.snap @@ -58,6 +58,7 @@ exports[`NewAnimalPage tests Should render Species/Acquisition page after lists "room": undefined, "selectedOption": undefined, "sire": undefined, + "sourceLocation": undefined, "species": undefined, } } @@ -139,11 +140,33 @@ exports[`NewAnimalPage tests Should render Species/Acquisition page after lists "room": undefined, "selectedOption": undefined, "sire": undefined, + "sourceLocation": undefined, "species": undefined, } } preventNext={[Function]} - reloadLocations={[Function]} + sourceLocationList={ + Array [ + Object { + "id": 0, + "label": "1001 - New Iberia Research Center U of LA at Lafayette, Lafayette, LA, USA", + "rowId": 1593, + "value": 1001, + }, + Object { + "id": 1, + "label": "1009 - Chimp Haven, Keithville, LA, USA", + "rowId": 1601, + "value": 1009, + }, + Object { + "id": 2, + "label": "1010 - SNBL USA Ltd., Everett, WA, USA", + "rowId": 1602, + "value": 1010, + }, + ] + } /> @@ -237,6 +260,7 @@ exports[`NewAnimalPage tests Should render Species/Acquisition page after lists "room": undefined, "selectedOption": undefined, "sire": undefined, + "sourceLocation": undefined, "species": undefined, } } @@ -346,6 +370,7 @@ exports[`NewAnimalPage tests Should render demographics page 1`] = ` "label": "14022", "value": "14022", }, + "sourceLocation": undefined, "species": Object { "arcSpeciesCode": "PC", "id": 10, @@ -468,6 +493,7 @@ exports[`NewAnimalPage tests Should render demographics page 1`] = ` "label": "14022", "value": "14022", }, + "sourceLocation": undefined, "species": Object { "arcSpeciesCode": "PC", "id": 10, @@ -531,6 +557,16 @@ exports[`NewAnimalPage tests Should render demographics page 1`] = ` /> +
+
+
+
{ acquisitionTypeList={ lists.acquisitionTypeList } disabled={ false } handleDataChange={ () => { } } + handleNumAnimalChange={ () => {} } newAnimalData={ newAnimalData } + numAnimals={ undefined } preventNext={ () => { return false } } - numAnimals= { undefined } - handleNumAnimalChange={ () => {} } /> ) @@ -25,18 +25,17 @@ describe('AcquisitionPanel tests', () => { test('Should render the AcquisitionPanel for multiple animal acquisition', () => { const newAnimalData = { ...(NewAnimalState().newAnimalData), - species: {value: 'HAM', arcSpeciesCode: 'MA' } - } - + species: { value: 'HAM', arcSpeciesCode: 'MA' } } + const wrapper = shallow( { } } + handleNumAnimalChange={ () => {} } newAnimalData={ newAnimalData } - preventNext={ () => { return false } } numAnimals={ 2 } - handleNumAnimalChange={ () => {} } + preventNext={ () => { return false } } /> ) diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/components/SummaryPanel.test.jsx b/snprc_ehr/src/client/NewAnimalPage/tests/components/SummaryPanel.test.jsx index 6ec73d0d4..954a9b3c9 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/components/SummaryPanel.test.jsx +++ b/snprc_ehr/src/client/NewAnimalPage/tests/components/SummaryPanel.test.jsx @@ -9,11 +9,11 @@ describe('AcquisitionPanel tests', () => { const wrapper = shallow( ) expect(wrapper).toMatchSnapshot() diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/Modals.test.jsx.snap b/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/Modals.test.jsx.snap index b99e30577..66ef85ee9 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/Modals.test.jsx.snap +++ b/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/Modals.test.jsx.snap @@ -166,6 +166,7 @@ exports[`Modal tests Should render the SaveModal 1`] = ` "room": undefined, "selectedOption": undefined, "sire": undefined, + "sourceLocation": undefined, "species": undefined, } } diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/SummaryPanel.test.jsx.snap b/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/SummaryPanel.test.jsx.snap index ee2ef85b5..b95ac3b70 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/SummaryPanel.test.jsx.snap +++ b/snprc_ehr/src/client/NewAnimalPage/tests/components/__snapshots__/SummaryPanel.test.jsx.snap @@ -378,7 +378,7 @@ exports[`AcquisitionPanel tests Should render the SummaryPanel 1`] = ` title="Owner" /> } - placement="left" + placement="right" trigger={ Array [ "hover", @@ -600,7 +600,7 @@ exports[`AcquisitionPanel tests Should render the SummaryPanel for multiple acqu } + placement="left" trigger={ Array [ "hover", @@ -928,7 +929,7 @@ exports[`AcquisitionPanel tests Should render the SummaryPanel for multiple acqu title="Owner" /> } - placement="left" + placement="right" trigger={ Array [ "hover", diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/apiTestData.js b/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/apiTestData.js index 930ff8e66..26efbb60d 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/apiTestData.js +++ b/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/apiTestData.js @@ -578,3 +578,82 @@ export const valid_bd_status = { }], rowCount: 3 } + +export const sourceLocations = { + schemaName: ['ehr_lookups'], + queryName: 'sourceLocations', + rows: [{ + data: { + SourceState: { + value: 'LA' + }, + SourceCountry: { + value: 'USA' + }, + code: { + value: 1001.00 + }, + meaning: { + value: 'New Iberia Research Center U of LA at Lafayette' + }, + description: { + value: null + }, + SourceCity: { + value: 'Lafayette' + }, + rowid: { + value: 1593 + } + } + }, { + data: { + SourceState: { + value: 'LA' + }, + SourceCountry: { + value: 'USA' + }, + code: { + value: 1009.00 + }, + meaning: { + value: 'Chimp Haven' + }, + description: { + value: null + }, + SourceCity: { + value: 'Keithville' + }, + rowid: { + value: 1601 + } + } + }, { + data: { + SourceState: { + value: 'WA' + }, + SourceCountry: { + value: 'USA' + }, + code: { + value: 1010.00 + }, + meaning: { + value: 'SNBL USA Ltd.' + }, + description: { + value: '6605 Merrill Creek Pkwy' + }, + SourceCity: { + value: 'Everett' + }, + rowid: { + value: 1602 + } + } + }], + rowCount: 3 +} diff --git a/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/lists.js b/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/lists.js index 09d619d91..61630f1b3 100644 --- a/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/lists.js +++ b/snprc_ehr/src/client/NewAnimalPage/tests/fixtures/lists.js @@ -15,6 +15,8 @@ export default { { id: 1, value: 2, label: 'Corn feed', species: null }], locationList: [{ id: 0, value: '1.01', label: '1.01', arcSpeciesCode: 'PC', maxCages: 42 }, { id: 1, value: '1.02', label: '1.02', arcSpeciesCode: 'PC', maxCages: 43 }], + sourceLocationList: [{ id: 0, value: '1001.00', label: '1001.00', rowId: 42 }, + { id: 1, value: '1002.00', label: '1002.00', rowId: 43 }], potentialDamList: [{ id: 0, value: '12924', label: '12924', ArcSpeciesCode: 'PC', Age: 24.7 }, { id: 1, value: '12925', label: '12925', ArcSpeciesCode: 'PC', Age: 21.8 }], potentialSireList: [{ id: 0, value: '14022', label: '14022', ArcSpeciesCode: 'PC', Age: 22.8 }, diff --git a/snprc_ehr/src/client/Shared/api/__mocks__/api.js b/snprc_ehr/src/client/Shared/api/__mocks__/api.js index b03aefc19..b65f5cf85 100644 --- a/snprc_ehr/src/client/Shared/api/__mocks__/api.js +++ b/snprc_ehr/src/client/Shared/api/__mocks__/api.js @@ -4,7 +4,7 @@ import { NewAnimalData } from '../../../BirthRecordReport/tests/fixtures/apiTest import { CurrentSpeciesLookup, AccountLookup, validInstitutions, ValidDiet, AcquisitionTypesLookup, valid_bd_status, PotentialDams, PotentialSires, ActiveLocationsAll, colonyGroups, - ProtocolLookup, pedigreeGroups + ProtocolLookup, pedigreeGroups, sourceLocations } from '../../../NewAnimalPage/tests/fixtures/apiTestData' export const request = ({ schemaName, queryName, parameters = {}, viewName = '', sort = '', columns = [], filterArray = [] }) => { @@ -58,5 +58,8 @@ export const executeSql = ({ schemaName, sql, parameters = {}, sort = '' }) => { if (sql.indexOf('FROM snprc_ehr.colonyGroups') > 0) { resolve(colonyGroups) } + if (sql.indexOf('FROM ehr_lookups.source') > 0) { + resolve(sourceLocations) + } }) } diff --git a/snprc_ehr/src/client/Shared/components/InfoPanel.jsx b/snprc_ehr/src/client/Shared/components/InfoPanel.jsx index 8f93da483..5c1503caa 100644 --- a/snprc_ehr/src/client/Shared/components/InfoPanel.jsx +++ b/snprc_ehr/src/client/Shared/components/InfoPanel.jsx @@ -5,7 +5,7 @@ export default class InfoPanel extends React.PureComponent { const messages = this.props.messages && this.props.messages const errorMessages = this.props.errorMessages && this.props.errorMessages const infoMessages = this.props.infoMessages && this.props.infoMessages - const includeBullets = this.props.includeBullets + const { includeBullets } = this.props return ( <> @@ -34,9 +34,9 @@ export default class InfoPanel extends React.PureComponent { && (
- { infoMessages.map( (message, index) => { + { infoMessages.map((message, index) => { const msg = includeBullets ? `${index + 1} ) ${message.value}` : message.value - return
{ msg }
+ return
{ msg }
}) }
diff --git a/snprc_ehr/src/client/Shared/components/LoadingSpinner.jsx b/snprc_ehr/src/client/Shared/components/LoadingSpinner.jsx index 24254f641..67120c171 100644 --- a/snprc_ehr/src/client/Shared/components/LoadingSpinner.jsx +++ b/snprc_ehr/src/client/Shared/components/LoadingSpinner.jsx @@ -1,18 +1,17 @@ import React from 'react' export class LoadingSpinner extends React.PureComponent { - static defaultProps = { - msg: 'Loading...', - wrapperClassName: 'loading-spinner', - }; - - render() { +render() { const { msg, wrapperClassName } = this.props return ( - -