asb_cloud_front/src/pages/Analytics/Statistics.jsx

238 lines
9.6 KiB
React
Raw Normal View History

import { Table as RawTable, Typography } from 'antd'
import { Fragment, memo, useCallback, useContext, useEffect, useState } from 'react'
import { IdWellContext } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { WellSelector } from '@components/selectors/WellSelector'
import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table'
import { OperationStatService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils'
import '@styles/index.css'
import '@styles/statistics.less'
const { Text } = Typography
const { Summary } = RawTable
const { Cell, Row } = Summary
const numericRender = makeNumericRender()
const speedNumericRender = (section) => numericRender(section?.speed)
const makeSectionSorter = (key, name) => (a, b) => (a?.[key]?.[name] ?? 0) - (b?.[key]?.[name] ?? 0)
export const makeSectionColumn = (title, key, { speedRender } = {}) => makeGroupColumn(title, [
makeNumericColumn('Проходка', key, null, null, (section => numericRender(section?.depth)), 100, {
sorter: makeSectionSorter(key, 'depth'),
}),
makeNumericColumn('Время', key, null, null, (section => numericRender(section?.time)), 100, {
sorter: makeSectionSorter(key, 'time'),
}),
makeNumericColumn((<>V<sub>рейсовая</sub></>), key, null, null, speedRender ?? speedNumericRender, 100, {
sorter: makeSectionSorter(key, 'speed'),
}),
])
export const defaultColumns = [
//makeTextColumn('Куст', 'cluster', null, null, null, { fixed: 'left', width: 100 }),
makeTextColumn('Скважина', 'caption', null, null, null, { fixed: 'left', width: 100 }),
]
const scrollSettings = { scrollToFirstRowOnChange: true, x: 100, y: 200 }
const summaryColSpan = 1 /// TODO: Когда добавится куст изменить на 2
const getWellData = async (wellsList) => {
const stats = arrayOrDefault(await OperationStatService.getWellsStat(wellsList))
const wellData = stats.map((well) => {
const stat = {
// cluster: null,
caption: well.caption,
}
well.sections?.forEach(({ id, fact }) => {
if (!fact) return
stat[`section_${id}`] = {
time: (+new Date(fact.end) - +new Date(fact.start)) / 3600_000,
depth: fact.wellDepthEnd - fact.wellDepthStart,
speed: fact.routeSpeed,
}
})
return stat
})
return wellData
}
export const Statistics = memo(() => {
const [sectionTypes, setSectionTypes] = useState([])
const [avgColumns, setAvgColumns] = useState(defaultColumns)
const [cmpColumns, setCmpColumns] = useState(defaultColumns)
const [isAvgTableLoading, setIsAvgTableLoading] = useState(false)
const [isCmpTableLoading, setIsCmpTableLoading] = useState(false)
const [isPageLoading, setIsPageLoading] = useState(false)
const [avgWells, setAvgWells] = useState([])
const [cmpWells, setCmpWells] = useState([])
const [avgData, setAvgData] = useState([])
const [cmpData, setCmpData] = useState([])
const [avgRow, setAvgRow] = useState({})
const idWell = useContext(IdWellContext)
const cmpSpeedRender = useCallback((key) => (section) => {
let spanClass = ''
// Дополнительная проверка на "null" необходима, чтобы значение "0" не стало исключением
if ((avgRow[key]?.speed ?? null) !== null && (section?.speed ?? null) !== null) {
const avgSpeed = avgRow[key].speed - section.speed
if (avgSpeed < 0)
spanClass = 'high-efficienty'
else if (avgSpeed > 0)
spanClass = 'low-efficienty'
}
return (
<span className={spanClass}>
{numericRender(section?.speed)}
</span>
)
}, [avgRow])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const types = await WellOperationService.getSectionTypes(idWell)
setSectionTypes(Object.entries(types))
},
setIsPageLoading,
`Не удалось получить типы секции`,
`Получение списка возможных секций`,
), [idWell])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const filteredSections = avgData?.length > 0 ? sectionTypes.filter(([id, _]) => avgData.some((row) => `section_${id}` in row)) : sectionTypes
setAvgColumns([
...defaultColumns,
...filteredSections.map(([id, name]) => makeSectionColumn(name, `section_${id}`)),
])
setCmpColumns([
...defaultColumns,
...filteredSections.map(([id, name]) => makeSectionColumn(name, `section_${id}`, {
speedRender: cmpSpeedRender(`section_${id}`)
}))
])
},
setIsPageLoading,
'Не удалось установить необходимые столбцы'
), [sectionTypes, avgData, cmpSpeedRender])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const avgData = await getWellData(avgWells)
setAvgData(avgData)
const avgRow = {}
avgData.forEach((row) => row && Object.keys(row).forEach((key) => {
if (!key.startsWith('section_')) return
if (!avgRow[key]) avgRow[key] = { depth: 0, time: 0, speed: 0, count: 0 }
avgRow[key].depth += row[key].depth ?? 0
avgRow[key].time += row[key].time ?? 0
avgRow[key].speed += row[key].speed ?? 0
avgRow[key].count++
}))
Object.values(avgRow).forEach((section) => section.speed /= section.count)
setAvgRow(avgRow)
},
setIsAvgTableLoading,
'Не удалось загрузить данные для расчёта средних значений',
), [avgWells])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const cmpData = await getWellData(cmpWells)
setCmpData(cmpData)
},
setIsCmpTableLoading,
'Не удалось получить скважины для сравнения',
), [cmpWells])
const getStatisticsAvgSummary = useCallback((data) => (
<Summary fixed={'bottom'}>
<Row>
<Cell index={0} colSpan={summaryColSpan}>
<Text>Итого:</Text>
</Cell>
{sectionTypes.map(([id, _], i) => (
<Fragment key={id ?? i}>
<Cell index={3 * i + summaryColSpan}>
<Text>{numericRender(avgRow[`section_${id}`]?.depth)}</Text>
</Cell>
<Cell index={3 * i + summaryColSpan + 1}>
<Text>{numericRender(avgRow[`section_${id}`]?.time)}</Text>
</Cell>
<Cell index={3 * i + summaryColSpan + 2}>
<Text>{numericRender(avgRow[`section_${id}`]?.speed)}</Text>
</Cell>
</Fragment>
))}
</Row>
</Summary>
), [avgRow, sectionTypes])
return (
<div className={'statistics-page'}>
<LoaderPortal show={isPageLoading}>
<h2 style={{ padding: '10px' }}>Расчёт средних значений без Цифровой буровой</h2>
<div className={'average-table'}>
<div className={'well-selector'}>
<span className={'well-selector-label'}>Выберите скважины для расчёта средних значений:</span>
<WellSelector
idWell={idWell}
value={avgWells}
style={{ flex: 100 }}
onChange={setAvgWells}
placeholder={'Ничего не выбрано'}
/>
</div>
<Table
bordered
size={'small'}
pagination={false}
columns={avgColumns}
dataSource={avgData}
scroll={scrollSettings}
loading={isAvgTableLoading}
summary={getStatisticsAvgSummary}
/>
</div>
<div className={'compare-table'}>
<div className={'well-selector'}>
<span className={'well-selector-label'}>Выберите скважины сравнения:</span>
<WellSelector
idWell={idWell}
value={cmpWells}
style={{ flex: 100 }}
onChange={setCmpWells}
placeholder={'Ничего не выбрано'}
/>
</div>
<Table
bordered
size={'small'}
pagination={false}
columns={cmpColumns}
dataSource={cmpData}
scroll={scrollSettings}
loading={isCmpTableLoading}
/>
</div>
</LoaderPortal>
</div>
)
})
export default Statistics