diff --git a/api/templates/study-sites-template.csv b/api/templates/study-sites-template.csv index d571a77d..ac4889bd 100644 --- a/api/templates/study-sites-template.csv +++ b/api/templates/study-sites-template.csv @@ -1,3 +1,3 @@ -Proposal ID,Principal Investigator,Site Number,Site ID,CTSA ID,Site Name, Date Protocol Sent (mm-dd-yyyy),Date Contract Sent (mm-dd-yyyy),Date of IRB Submission (mm-dd-yyyy),Date of IRB Approval (mm-dd-yyyy),Date of Contract Execution (mm-dd-yyyy),Last Patient First Visit (mm-dd-yyyy),Date of Site Activation (mm-dd-yyyy),First Patient First Visit (mm-dd-yyyy),Number of Patients Consented,Number of Patients Enrolled,Number of Patients Withdrawn,Number of Patients Expected,Queries per total data elements,Number of Protocol Deviations,data Element,lost To FollowUp -ProposalID,principalInvestigator,siteNumber,siteId,ctsaId,siteName,dateRegPacketSent,dateContractSent,dateIrbSubmission,dateIrbApproval,dateContractExecution,lpfv,dateSiteActivated,fpfv,patientsConsentedCount,patientsEnrolledCount,patientsWithdrawnCount,patientsExpectedCount,queriesCount,protocolDeviationsCount,dataElement,lostToFollowUp +Proposal ID,Principal Investigator,Site Number,Site ID,CTSA ID,Site Name, Date Protocol Sent (mm-dd-yyyy),Date Contract Sent (mm-dd-yyyy),Date of IRB Submission (mm-dd-yyyy),Date of IRB Approval (mm-dd-yyyy),Date of Contract Execution (mm-dd-yyyy),Last Patient First Visit (mm-dd-yyyy),Date of Site Activation (mm-dd-yyyy),First Patient First Visit (mm-dd-yyyy),Number of Patients Consented,Number of Patients Enrolled,Number of Patients Withdrawn,Number of Patients Expected,Number of Queries,Number of Protocol Deviations,lost To FollowUp +ProposalID,principalInvestigator,siteNumber,siteId,ctsaId,siteName,dateRegPacketSent,dateContractSent,dateIrbSubmission,dateIrbApproval,dateContractExecution,lpfv,dateSiteActivated,fpfv,patientsConsentedCount,patientsEnrolledCount,patientsWithdrawnCount,patientsExpectedCount,queriesCount,protocolDeviationsCount,lostToFollowUp ,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/frontend/src/components/Tables/DetailPanels/SiteDetailPanel.js b/frontend/src/components/Tables/DetailPanels/SiteDetailPanel.js index 9aab1b69..0f9c1796 100644 --- a/frontend/src/components/Tables/DetailPanels/SiteDetailPanel.js +++ b/frontend/src/components/Tables/DetailPanels/SiteDetailPanel.js @@ -4,12 +4,14 @@ import { DetailPanel } from './DetailPanel' import { StarBullet } from '../../Bullets' import { formatDate } from '../../../utils/DateFormat' +const invalidDisplay = 'N/A' + export const dayCount = (startDate, endDate) => { if (startDate && endDate) { const num = Math.round((new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24)) return `${ num } day${ num === 1 ? '' : 's' }` } else { - return 'N/A' + return invalidDisplay } } @@ -17,10 +19,10 @@ export const displayRatio = (a, b, precision = 2) => { a = parseInt(a) b = parseInt(b) if ( !a || !b ) { - return 'N/A' + return invalidDisplay } if (a === 0) { - if (b === 0) return `N/A` + if (b === 0) return invalidDisplay return `0% (${ a }/${ b })` } return b !== 0 @@ -28,9 +30,16 @@ export const displayRatio = (a, b, precision = 2) => { : `N/A` } +const displayRatioAsWholeNumberString = (a, b) => { + return b === 0 ? invalidDisplay : `${ Math.round(a / b) } ≈ ${ a } / ${ b }` +} + export const SiteDetailPanel = props => { const { - siteName, dateRegPacketSent, dateContractSent, dateIrbSubmission, dateIrbApproval, dateContractExecution, dateSiteActivated, lpfv, fpfv, patientsConsentedCount, patientsEnrolledCount, patientsWithdrawnCount, patientsExpectedCount, queriesCount, protocolDeviationsCount, dataElement + siteName, dateRegPacketSent, dateContractSent, dateIrbSubmission, dateIrbApproval, + dateContractExecution, dateSiteActivated, lpfv, fpfv, patientsConsentedCount, patientsEnrolledCount, + patientsWithdrawnCount, patientsExpectedCount, queriesCount, protocolDeviationsCount, dataElement, + queriesPerConsentedPatient, } = props return ( @@ -87,7 +96,7 @@ export const SiteDetailPanel = props => { - + diff --git a/frontend/src/components/Tables/SitesTable.js b/frontend/src/components/Tables/SitesTable.js index 6aeba484..a77ee05f 100644 --- a/frontend/src/components/Tables/SitesTable.js +++ b/frontend/src/components/Tables/SitesTable.js @@ -4,6 +4,12 @@ import { StoreContext } from '../../contexts/StoreContext' import { SiteDetailPanel, dayCount, displayRatio } from './DetailPanels' import { EnrollmentBar } from '../Widgets/EnrollmentBar' +const invalidDisplay = 'N/A' + +const ratioAsWholeNumberString = (a, b) => { + return b === 0 ? invalidDisplay : Math.round(a / b) +} + export const SitesTable = props => { let { title, sites } = props const [store, ] = useContext(StoreContext) @@ -28,7 +34,7 @@ export const SitesTable = props => { site.actualToExpectedRandomizedPtRatio = displayRatio(site.patientsEnrolledCount, site.patientsExpectedCount) site.ratioRandomizedPtsDropout = displayRatio(site.patientsWithdrawnCount, site.patientsEnrolledCount) site.majorProtocolDeviationsPerRandomizedPt = displayRatio( site.protocolDeviationsCount, site.patientsEnrolledCount) - site.queriesPerDataElement = displayRatio(site.queriesCount, site.dataElement ) + site.queriesPerConsentedPatient = ratioAsWholeNumberString(site.queriesCount, site.patientsConsentedCount ) }) } }, [sites, store.proposals]) @@ -59,7 +65,7 @@ export const SitesTable = props => { return ( { { title: 'Patients Enrolled', field: 'patientsEnrolledCount', hidden: true, }, { title: 'Patients Withdrawn', field: 'patientsWithdrawnCount', hidden: true, }, { title: 'Patients Expected', field: 'patientsExpectedCount', hidden: true, }, - { title: 'Queries Count', field: 'queriesCount', hidden: true, }, { title: 'Protocol Deviations', field: 'protocolDeviationsCount', hidden: true, }, - { title: 'Data Element', field: 'dataElement', hidden: true, }, { title: 'Lost to Follow Up', field: 'lostToFollowUp', hidden: true, }, { title: 'Protocol to FPFV', field: 'protocolToFpfv', hidden: true, }, { title: 'Contract Execution Time', field: 'contractExecutionTime', hidden: true, }, @@ -96,7 +100,12 @@ export const SitesTable = props => { { title: 'Actual to expected randomized patient ratio', field: 'actualToExpectedRandomizedPtRatio', hidden: true, }, { title: 'Ratio of randomized patients that dropout of the study', field: 'ratioRandomizedPtsDropout', hidden: true, }, { title: 'Major Protocol deviations per randomized patient', field: 'majorProtocolDeviationsPerRandomizedPt', hidden: true, }, - { title: 'Queries per data element', field: 'queriesPerDataElement', hidden: true, } + { title: 'Number of Queries', field: 'queriesCount', hidden: true, }, + { + title: 'Queries per patient', + render: row => row.queriesPerConsentedPatient, + hidden: true, + }, ] } data={ sites } diff --git a/frontend/src/views/Studies/CombinedMetrics.js b/frontend/src/views/Studies/CombinedMetrics.js index 136d361f..ddfd2f19 100644 --- a/frontend/src/views/Studies/CombinedMetrics.js +++ b/frontend/src/views/Studies/CombinedMetrics.js @@ -42,35 +42,69 @@ export const CombinedMetrics = ({ study, studyProfile, sites }) => { return sites.reduce((sum, site) => sum + site[key], 0) } - const ratioString = (a, b, precision = 2) => { - return b === 0 ? invalidDisplay : `${ (100 * a/b).toFixed(precision) }% (${ a }/${ b })` + const ratioAsPercentString = (a, b, precision = 2) => { + return b === 0 ? invalidDisplay : `${ (100 * a/b).toFixed(precision) }% ≈ ${ a } / ${ b }` } - const item = (label, value) => ( + const ratioAsWholeNumberString = (a, b) => { + return b === 0 ? invalidDisplay : `${ Math.round(a / b) } ≈ ${ a } / ${ b }` + } + + const Metric = React.useCallback(({ label, value }) => ( - ) + ), []) return ( - { item('Activation (protocol to FPFV)', dayString(averageDays('dateRegPacketSent', 'fpfv'))) } - { item('Contract execution time', dayString(averageDays('dateContractSent', 'dateContractExecution'))) } - { item('sIRB approval time', dayString(averageDays('dateIrbSubmission', 'dateIrbApproval'))) } - { item('Site open to FPFV', dayString(averageDays('dateSiteActivated', 'fpfv'))) } - { item('Site open to LPFV', dayString(averageDays('dateSiteActivated', 'lpfv'))) } + + + + + - { item('Percent of consented patients randomized', ratioString(sum('patientsEnrolledCount'), sum('patientsConsentedCount'))) } - { item('Actual to expected randomized patient ratio', ratioString(sum('patientsEnrolledCount'), sum('patientsExpectedCount'))) } - { item('Ratio of randomized patients that dropped out of the study', ratioString(sum('patientsWithdrawnCount'), sum('patientsEnrolledCount'))) } - { item('Major protocol deviations per randomized patients', ratioString(sum('protocolDeviationsCount'), sum('patientsEnrolledCount'))) } - { item('Queries per data element', ratioString(sum('queriesCount'), sum('dataElement'))) } + + + + +