From 17cbe374adeaa17cea737ef308b2ce6a15958baa Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 21 Jul 2021 15:36:08 +0500 Subject: [PATCH] =?UTF-8?q?=D0=93=D1=80=D0=B0=D1=84=D0=B8=D0=BA-=D0=BF?= =?UTF-8?q?=D0=BE=D0=BD=D1=87=D0=B8=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/AnalysisDepthToInterval.jsx | 2 +- src/components/AnalysisOperationTime.jsx | 60 ++++++ .../charts/ChartDepthToIntervalBase.tsx | 5 +- src/components/charts/ChartOperationTime.jsx | 67 +++++++ .../charts/ChartOperationTimeBase.tsx | 177 ++++++++++++++++++ src/pages/Analysis.jsx | 23 ++- 6 files changed, 326 insertions(+), 8 deletions(-) create mode 100644 src/components/AnalysisOperationTime.jsx create mode 100644 src/components/charts/ChartOperationTime.jsx create mode 100644 src/components/charts/ChartOperationTimeBase.tsx diff --git a/src/components/AnalysisDepthToInterval.jsx b/src/components/AnalysisDepthToInterval.jsx index c0f846c..840206c 100644 --- a/src/components/AnalysisDepthToInterval.jsx +++ b/src/components/AnalysisDepthToInterval.jsx @@ -40,7 +40,7 @@ export function AnalysisDepthToInterval() { AnalyticsService.getWellDepthToInterval(id, chartInterval) .then(handleReceiveDepthToIntervalData) .catch(error => { - notify(`Не удалось получить данные для Анализа Глубина-День по скважине "${id}"`, + notify(`Не удалось получить данные для Анализа скорость проходки-интервал "${id}"`, 'warning') console.log(error) }) diff --git a/src/components/AnalysisOperationTime.jsx b/src/components/AnalysisOperationTime.jsx new file mode 100644 index 0000000..1bcd261 --- /dev/null +++ b/src/components/AnalysisOperationTime.jsx @@ -0,0 +1,60 @@ +import { useParams } from "react-router-dom" +import { DatePicker, ConfigProvider } from 'antd'; +import notify from "../components/notify" +import { useState, useEffect } from 'react' +import { AnalyticsService } from '../services/api' +import { ChartOperationTime } from './charts/ChartOperationTime' +import locale from "antd/lib/locale/ru_RU"; + +const { RangePicker } = DatePicker + +const lines = [{ labelAccessorName: "processName", pieceAccessorName: "duration" }] + +export function AnalysisOperationTime() { + let { id } = useParams() + const [operationTimeData, setOperationTimeData] = useState([]) + const [loader, setLoader] = useState(false) + const [range, setRange] = useState([]) + + let begin = null + let end = null + + const onChangeRange = (range) => { + setRange(range) + } + + const handleReceiveOperationTimeData = (data) => { + setOperationTimeData(data) + } + + useEffect(() => { + setLoader(true) + if (range?.length > 1) { + begin = range[0].toISOString() + end = range[1].toISOString() + } + AnalyticsService.getOperationsSummary(id, begin, end) + .then(handleReceiveOperationTimeData) + .catch(error => { + notify(`Не удалось получить данные для Анализа Операция-Время по скважине "${id}" за период с ${begin} по ${end}`, + 'warning') + console.log(error) + }) + .finally(setLoader(false)) + }, [id, range]) + + return ( + <> + + + + + + ) +} \ No newline at end of file diff --git a/src/components/charts/ChartDepthToIntervalBase.tsx b/src/components/charts/ChartDepthToIntervalBase.tsx index e043091..9b53d50 100644 --- a/src/components/charts/ChartDepthToIntervalBase.tsx +++ b/src/components/charts/ChartDepthToIntervalBase.tsx @@ -220,8 +220,5 @@ export const ChartDepthToIntervalBase: React.FC = ({ options chart.update() }, [chart, dataParams, options]) - return () + return () } \ No newline at end of file diff --git a/src/components/charts/ChartOperationTime.jsx b/src/components/charts/ChartOperationTime.jsx new file mode 100644 index 0000000..85dc6a3 --- /dev/null +++ b/src/components/charts/ChartOperationTime.jsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from 'react' +import { ChartOpertationTimeBase } from './ChartOperationTimeBase' +import moment from 'moment' + +export const CreateLabels = (lineConfig) => { + let labels = [] + return labels +} + +export const CreateData = (lineConfig) => { + let datasets = { + label: lineConfig.label, + data: [], + backgroundColor: [ + '#f00', '#ff0', '#f0f', '#0ff', '#00f', '#0f0' + ], + } + return datasets +} + +export const ChartOperationTime = ({ lines, data, rangeDate }) => { + const [opertationTimeDataParams, setOpertationTimeDataParams] = useState({ data: { labels: [], datasets: [] } }) + + useEffect(() => { + if ((!lines) + || (!data)) + return + + let newLabels = lines.map(lineCfg => { + let labels = CreateLabels(lineCfg) + if (data.length !== 0) + labels = data.map(dataItem => { + return dataItem[lineCfg.labelAccessorName] + }) + return labels + }) + + let newDatasets = lines.map(lineCfg => { + let datasets = CreateData(lineCfg) + if (data.length !== 0) + datasets.data = data.map(dataItem => { + return dataItem[lineCfg.pieceAccessorName] + }) + return datasets + }) + + let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null + let startDate = rangeDate ? rangeDate[0] : moment() + let newParams = { + xInterval: interval, + xStart: startDate, + data: { + labels: newLabels, + datasets: newDatasets + } + } + setOpertationTimeDataParams(newParams) + + console.log(newParams) + + }, [data, lines, rangeDate]) + + return (<> + + + ) +} \ No newline at end of file diff --git a/src/components/charts/ChartOperationTimeBase.tsx b/src/components/charts/ChartOperationTimeBase.tsx new file mode 100644 index 0000000..5d9e96c --- /dev/null +++ b/src/components/charts/ChartOperationTimeBase.tsx @@ -0,0 +1,177 @@ +import { useEffect, useRef, useState } from 'react' +import { + Chart, + ArcElement, + TimeScale, + Legend, + PointElement, + ChartData, + ChartTypeRegistry, + ChartOptions, + DoughnutController, +} from 'chart.js' +import 'chartjs-adapter-moment' +import ChartDataLabels from 'chartjs-plugin-datalabels' + +Chart.register(TimeScale, DoughnutController, PointElement, ArcElement, Legend, ChartDataLabels) + +const defaultOptions = { + responsive: true, + title: { + display: true, + position: "top", + text: "Doughnut Chart", + fontSize: 18, + fontColor: "#111" + }, + legend: { + display: true, + position: "bottom", + labels: { + fontColor: "#333", + fontSize: 16 + } + } +} + +export type ChartTimeData = ChartData + +export type ChartTimeDataParams = { + data: ChartTimeData, + xStart?: Date, + xInterval?: number, + displayLabels?: Boolean, +} + +export type ChartTimeBaseProps = { + dataParams: ChartTimeDataParams, + // TODO: Create good type for options + options?: ChartOptions | any, +} + +export type TimeParams = { + unit: String + stepSize: number +} + +const linesPerInterval = 32 + +export const timeUnitByInterval = (intervalSec: number): String => { + if (intervalSec <= 60) + return 'millisecond' + + if (intervalSec <= 32 * 60) + return 'second' + + if (intervalSec <= 32 * 60 * 60) + return 'minute' + + if (intervalSec <= 32 * 12 * 60 * 60) + return 'hour' + + if (intervalSec <= 32 * 24 * 60 * 60) + return 'day' + + if (intervalSec <= 32 * 7 * 24 * 60 * 60) + return 'week' + + if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60) + return 'month' + + if (intervalSec <= 32 * 121.75 * 24 * 60 * 60) + return 'quarter' + else + return 'year' +} + +export const timeParamsByInterval = (intervalSec: number): TimeParams => { + let stepSize = intervalSec + let unit = timeUnitByInterval(intervalSec) + + switch (unit) { + case "millisecond": + stepSize *= 1000 + break; + case "second": + //stepSize *= 1 + break; + case "minute": + stepSize /= 60 + break; + case "hour": + stepSize /= 60 * 60 + break; + case "day": + stepSize /= 24 * 60 * 60 + break; + case "week": + stepSize /= 7 * 24 * 60 * 60 + break; + case "month": + stepSize /= 30 * 24 * 60 * 60 + break; + case "quarter": + stepSize /= 91 * 24 * 60 * 60 + break; + case "year": + stepSize /= 365.25 * 24 * 60 * 60 + break; + } + + stepSize = Math.round(stepSize / linesPerInterval) + stepSize = stepSize > 0 ? stepSize : 1; + return { unit, stepSize } +} + +export const ChartOpertationTimeBase: React.FC = ({ options, dataParams }) => { + const chartRef = useRef(null) + const [chart, setChart] = useState() + + useEffect(() => { + if ((chartRef.current) && (!chart)) { + let thisOptions = {} + Object.assign(thisOptions, defaultOptions, options) + + let newChart = new Chart(chartRef.current, { + type: 'doughnut', + plugins: [ChartDataLabels], + options: thisOptions, + data: dataParams.data + }) + setChart(newChart) + + return () => chart?.destroy() + } + }, [chart, options, dataParams]) + + useEffect(() => { + if (!chart) + return + + chart.data = dataParams.data + chart.options.aspectRatio = options?.aspectRatio + if (dataParams.xStart) { + let interval = Number(dataParams.xInterval ?? 600) + let start = new Date(dataParams.xStart) + let end = new Date(dataParams.xStart) + end.setSeconds(end.getSeconds() + interval) + let { unit, stepSize } = timeParamsByInterval(interval) + + if (chart.options.scales?.x) { + chart.options.scales.x.max = end.getTime() + chart.options.scales.x.min = start.getTime() + chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true + chart.options.scales.x.time.unit = unit + chart.options.scales.x.time.stepSize = stepSize + } + } + + chart.update() + }, [chart, dataParams, options]) + + return () +} \ No newline at end of file diff --git a/src/pages/Analysis.jsx b/src/pages/Analysis.jsx index 71c823f..9f8956d 100644 --- a/src/pages/Analysis.jsx +++ b/src/pages/Analysis.jsx @@ -1,13 +1,30 @@ import { AnalysisDepthToDay } from '../components/AnalysisDepthToDay' import { AnalysisDepthToInterval } from '../components/AnalysisDepthToInterval' +import { AnalysisOperationTime } from '../components/AnalysisOperationTime' +import { Row, Col } from 'antd' export default function Analysis() { return ( <> - -
 
- + + + + + + + + + + + + + + + + + + ) } \ No newline at end of file