Skip to content

Commit

Permalink
feat: added biology view to dashboard - Ref gestion-de-projet#2678 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ManelleG authored Sep 30, 2024
1 parent f42ea13 commit ac3dfee
Show file tree
Hide file tree
Showing 9 changed files with 691 additions and 22 deletions.
496 changes: 496 additions & 0 deletions src/components/Dashboard/BiologyList/index.tsx

Large diffs are not rendered by default.

49 changes: 38 additions & 11 deletions src/components/DataTable/DataTableObservation.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useContext } from 'react'

import { CircularProgress, Grid, Typography, TableRow } from '@mui/material'
import { CircularProgress, Grid, Typography, TableRow, IconButton } from '@mui/material'
import SearchIcon from 'assets/icones/search.svg?react'

import { TableCellWrapper } from 'components/ui/TableCell/styles'

import DataTable from 'components/DataTable/DataTable'
Expand All @@ -20,6 +22,8 @@ type DataTableObservationProps = {
page?: number
setPage?: (page: number) => void
total?: number
showIpp?: boolean
groupId?: string
}
const DataTableObservation: React.FC<DataTableObservationProps> = ({
loading,
Expand All @@ -29,32 +33,40 @@ const DataTableObservation: React.FC<DataTableObservationProps> = ({
setOrderBy,
page,
setPage,
total
total,
showIpp,
groupId
}) => {
const { classes } = useStyles()

const columns: Column[] = [
{ label: `NDA${deidentified ? ' chiffré' : ''}`, align: 'left' },
...(showIpp ? [{ label: `IPP${deidentified ? ' chiffré' : ''}`, align: 'left' }] : []),
{ label: `NDA${deidentified ? ' chiffré' : ''}`, align: showIpp ? 'center' : 'left' },
{ label: 'Date de prélèvement', code: Order.DATE },
{ label: 'ANABIO', code: Order.ANABIO },
{ label: 'LOINC', code: Order.LOINC },
{ label: 'Résultat' },
{ label: 'Valeurs de référence' },
{ label: 'Unité exécutrice' }
]
].filter((elem) => elem !== null) as Column[]

return (
<DataTable columns={columns} order={orderBy} setOrder={setOrderBy} page={page} setPage={setPage} total={total}>
{!loading && observationsList?.length > 0 && (
<>
{observationsList.map((observation) => (
<DataTableObservationLine key={observation.id} observation={observation} />
<DataTableObservationLine
key={observation.id}
observation={observation}
showIpp={showIpp}
groupId={groupId}
/>
))}
</>
)}
{!loading && observationsList?.length < 1 && (
<TableRow className={classes.emptyTableRow}>
<TableCellWrapper colSpan={7} align="left">
<TableCellWrapper colSpan={columns.length} align="left">
<Grid container justifyContent="center">
<Typography variant="button">Aucun résultat de biologie à afficher</Typography>
</Grid>
Expand All @@ -63,7 +75,7 @@ const DataTableObservation: React.FC<DataTableObservationProps> = ({
)}
{loading && (
<TableRow className={classes.emptyTableRow}>
<TableCellWrapper colSpan={7} align="left">
<TableCellWrapper colSpan={columns.length} align="left">
<Grid container justifyContent="center">
<CircularProgress />
</Grid>
Expand All @@ -76,17 +88,20 @@ const DataTableObservation: React.FC<DataTableObservationProps> = ({

const formatValueRange = (value?: string | number, valueUnit?: string): string => {
if (value) {
return `${value} ${valueUnit || ''}`
return `${value} ${valueUnit ?? ''}`
}
return '_'
}

const DataTableObservationLine: React.FC<{
observation: CohortObservation
}> = ({ observation }) => {
showIpp?: boolean
groupId?: string
}> = ({ observation, showIpp, groupId }) => {
const { classes } = useStyles()
const appConfig = useContext(AppConfig)

const ipp = observation.IPP
const nda = observation.NDA
const date = observation.effectiveDateTime
const libelleANABIO = observation.code?.coding?.find(
Expand All @@ -100,7 +115,7 @@ const DataTableObservationLine: React.FC<{
)?.display
const result =
observation.valueQuantity?.value !== null
? `${observation.valueQuantity?.value} ${observation.valueQuantity?.unit || ''}`
? `${observation.valueQuantity?.value} ${observation.valueQuantity?.unit ?? ''}`
: '-'
const valueUnit = observation.valueQuantity?.unit ?? ''
const serviceProvider = observation.serviceProvider
Expand All @@ -111,10 +126,22 @@ const DataTableObservationLine: React.FC<{
valueUnit
)}`
: '-'
const groupIdSearch = groupId ? `?groupId=${groupId}` : ''

return (
<TableRow className={classes.tableBodyRows} key={observation.id}>
<TableCellWrapper align="left">{nda ?? 'Inconnu'}</TableCellWrapper>
{showIpp && (
<TableCellWrapper style={{ minWidth: 150 }}>
{ipp}
<IconButton
onClick={() => window.open(`/patients/${observation.idPatient}${groupIdSearch}`, '_blank')}
className={classes.searchIcon}
>
<SearchIcon height="15px" fill="#ED6D91" className={classes.iconMargin} />
</IconButton>
</TableCellWrapper>
)}
<TableCellWrapper align={showIpp ? 'center' : 'left'}>{nda ?? 'Inconnu'}</TableCellWrapper>
<TableCellWrapper>{date ? new Date(date).toLocaleDateString('fr-FR') : 'Date inconnue'}</TableCellWrapper>
<TableCellWrapper>
<Typography className={classes.libelle}>
Expand Down
3 changes: 2 additions & 1 deletion src/mappers/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ export enum ObservationParamsKeys {
DATE = 'date',
VALUE = 'value-quantity',
EXECUTIVE_UNITS = 'encounter.encounter-care-site',
ENCOUNTER_STATUS = 'encounter.status'
ENCOUNTER_STATUS = 'encounter.status',
IPP = 'subject.identifier'
}

export enum ImagingParamsKeys {
Expand Down
13 changes: 10 additions & 3 deletions src/services/aphp/callApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ type fetchObservationProps = {
signal?: AbortSignal
executiveUnits?: string[]
encounterStatus?: string[]
uniqueFacet?: 'subject'[]
'patient-identifier'?: string
}
export const fetchObservation = async (args: fetchObservationProps): FHIR_Bundle_Promise_Response<Observation> => {
const {
Expand All @@ -693,12 +695,14 @@ export const fetchObservation = async (args: fetchObservationProps): FHIR_Bundle
encounterStatus
} = args
const _sortDirection = sortDirection === Direction.DESC ? '-' : ''
let { _list } = args
let { _list, uniqueFacet } = args
const patientIdentifier = args['patient-identifier']

_list = _list ? _list.filter(uniq) : []
uniqueFacet = uniqueFacet ? uniqueFacet.filter(uniq) : []

// By default, all the calls to `/Observation` will have 'value-quantity-value=ge0,le0' and 'patient.active=true' in the parameters
let options: string[] = ['value-quantity=ge0,le0', 'subject.active=true']
let options: string[] = [`${ObservationParamsKeys.VALUE}=ge0,le0`, 'subject.active=true']
if (id) options = [...options, `_id=${id}`]
if (size !== undefined) options = [...options, `_count=${size}`]
if (offset) options = [...options, `_offset=${offset}`]
Expand All @@ -715,9 +719,12 @@ export const fetchObservation = async (args: fetchObservationProps): FHIR_Bundle
if (maxDate) options = [...options, `${ObservationParamsKeys.DATE}=le${maxDate}`] // eslint-disable-line
if (rowStatus) options = [...options, `${ObservationParamsKeys.VALIDATED_STATUS}=${BiologyStatus.VALIDATED}`] // eslint-disable-line
if (executiveUnits && executiveUnits.length > 0)
options = [...options, `encounter.encounter-care-site=${executiveUnits}`]
options = [...options, `${ObservationParamsKeys.EXECUTIVE_UNITS}=${executiveUnits}`]
if (encounterStatus && encounterStatus.length > 0)
options = [...options, `${ObservationParamsKeys.ENCOUNTER_STATUS}=${encounterStatus}`]
if (patientIdentifier) options = [...options, `${ObservationParamsKeys.IPP}=${patientIdentifier}`]
if (uniqueFacet && uniqueFacet.length > 0)
options = [...options, `unique-facet=${uniqueFacet.reduce(paramValuesReducer, '')}`]

if (_list && _list.length > 0) options = [...options, `_list=${_list.reduce(paramValuesReducer)}`]

Expand Down
113 changes: 110 additions & 3 deletions src/services/aphp/serviceCohorts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import {
FHIR_Bundle_Response,
CohortPMSI,
MedicationData,
CohortMedication
CohortMedication,
BiologyData,
CohortObservation
} from 'types'
import {
getGenderRepartitionMapAphp,
Expand All @@ -40,7 +42,8 @@ import {
fetchProcedure,
fetchClaim,
fetchMedicationRequest,
fetchMedicationAdministration
fetchMedicationAdministration,
fetchObservation
} from './callApi'

import apiBackend from '../apiBackend'
Expand All @@ -52,6 +55,7 @@ import {
ImagingStudy,
MedicationAdministration,
MedicationRequest,
Observation,
ParametersParameter,
Patient,
Procedure
Expand All @@ -65,7 +69,8 @@ import {
SearchByTypes,
ImagingFilters,
PMSIFilters,
MedicationFilters
MedicationFilters,
BiologyFilters
} from 'types/searchCriterias'
import services from '.'
import { ErrorDetails, SearchInputError } from 'types/error'
Expand Down Expand Up @@ -212,6 +217,19 @@ export interface IServiceCohorts {
signal?: AbortSignal
) => Promise<MedicationData>

/**
* Retourne la liste d'objets de biologie liés à une cohorte
*/
fetchBiologyList: (
options: {
deidentified: boolean
page: number
searchCriterias: SearchCriterias<BiologyFilters>
},
groupId?: string,
signal?: AbortSignal
) => Promise<BiologyData>

/**
* Permet de vérifier si le champ de recherche textuelle est correct
*
Expand Down Expand Up @@ -689,6 +707,95 @@ const servicesCohorts: IServiceCohorts = {
}
},

fetchBiologyList: async (options, groupId, signal) => {
const {
deidentified,
page,
searchCriterias: {
orderBy,
searchInput,
filters: { validatedStatus, nda, ipp, loinc, anabio, startDate, endDate, executiveUnits, encounterStatus }
}
} = options
try {
const atLeastAFilter =
!!searchInput ||
!!ipp ||
!!nda ||
!!startDate ||
!!endDate ||
executiveUnits.length > 0 ||
encounterStatus.length > 0 ||
(loinc && loinc.length > 0) ||
(anabio && anabio.length > 0)

const [biologyList, allBiologyList] = await Promise.all([
fetchObservation({
_list: groupId ? [groupId] : [],
size: 20,
offset: page ? (page - 1) * 20 : 0,
_sort: orderBy.orderBy,
sortDirection: orderBy.orderDirection,
_text: searchInput === '' ? '' : searchInput,
encounter: nda,
'patient-identifier': ipp,
signal: signal,
executiveUnits: executiveUnits.map((unit) => unit.id),
encounterStatus: encounterStatus.map(({ id }) => id),
uniqueFacet: ['subject'],
minDate: startDate ?? '',
maxDate: endDate ?? '',
loinc: loinc.map((code) => code.id).join(),
anabio: anabio.map((code) => code.id).join(),
rowStatus: validatedStatus
}),
atLeastAFilter
? fetchObservation({
size: 0,
signal: signal,
_list: groupId ? [groupId] : [],
uniqueFacet: ['subject'],
rowStatus: validatedStatus
})
: null
])

const _biologyList =
getApiResponseResources(biologyList as AxiosResponse<FHIR_Bundle_Response<Observation>, any>) ?? []
const filledBiologyList = await getResourceInfos(_biologyList, deidentified, groupId, signal)

const totalBiology = biologyList?.data?.resourceType === 'Bundle' ? biologyList.data.total : 0
const totalAllBiology =
allBiologyList?.data?.resourceType === 'Bundle' ? allBiologyList.data?.total ?? totalBiology : totalBiology

const totalPatientBiology =
biologyList?.data?.resourceType === 'Bundle'
? (getExtension(biologyList?.data?.meta, 'unique-subject') || { valueDecimal: 0 }).valueDecimal
: 0
const totalAllPatientsBiology =
allBiologyList !== null
? (getExtension(allBiologyList?.data?.meta, 'unique-subject') || { valueDecimal: 0 }).valueDecimal
: totalPatientBiology

return {
totalBiology: totalBiology ?? 0,
totalAllBiology: totalAllBiology ?? 0,
totalPatientBiology: totalPatientBiology ?? 0,
totalAllPatientsBiology: totalAllPatientsBiology ?? 0,
biologyList: filledBiologyList as CohortObservation[]
}
} catch (error) {
console.error('Erreur lors de la récupération de la liste de Biologie :', error)
return {
totalBiology: 0,
totalAllBiology: 0,
totalPatientBiology: 0,
totalAllPatientsBiology: 0,
biologyList: []
}
}
},

fetchImagingList: async (options, groupId, signal) => {
const {
deidentified,
Expand Down
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ export type MedicationData = {
medicationList: CohortMedication<MedicationRequest | MedicationAdministration>[]
}

export type BiologyData = {
totalBiology: number
totalAllBiology: number
totalPatientBiology: number
totalAllPatientsBiology: number
biologyList: CohortObservation[]
}

export type PatientData = {
patient?: CohortPatient
hospit?: (CohortEncounter | Encounter)[]
Expand Down Expand Up @@ -673,6 +681,8 @@ export type CohortQuestionnaireResponse = QuestionnaireResponse & {
export type CohortObservation = Observation & {
serviceProvider?: string
NDA?: string
IPP?: string
idPatient?: string
}

export type IPatientObservation<T extends CohortObservation> = {
Expand Down
1 change: 1 addition & 0 deletions src/types/searchCriterias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ export type BiologyFilters = GenericFilter & {
loinc: LabelObject[]
anabio: LabelObject[]
validatedStatus: boolean
ipp?: string
}

export type ImagingFilters = GenericFilter & {
Expand Down
Loading

0 comments on commit ac3dfee

Please sign in to comment.