Skip to content

Commit

Permalink
BITMAKER-3293 Stats module: Refactoring of stats components (#191)
Browse files Browse the repository at this point in the history
* Update folders structure of the frontend and refactoring project dashboard page.
  • Loading branch information
webtaken committed Jun 7, 2023
1 parent 76073eb commit b1c9f57
Show file tree
Hide file tree
Showing 11 changed files with 662 additions and 792 deletions.
249 changes: 249 additions & 0 deletions estela-web/src/components/Stats/ChartsSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import React, { Component } from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
ChartDataset,
ChartData,
} from "chart.js";
import { Bar } from "react-chartjs-2";
import { StatType, Spin as Spinner } from "../../shared";
import { GlobalStats, SpidersJobsStats } from "../../services";
import { Empty } from "antd";

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);

const getJobsDataset = (statsData: GlobalStats[]) => {
return [
{
label: "Finished",
data: statsData.map((statData) => statData.stats.jobs?.finishedJobs ?? 0),
backgroundColor: "#32C3A4",
},
{
label: "Running",
data: statsData.map((statData) => statData.stats.jobs?.runningJobs ?? 0),
backgroundColor: "#D1A34F",
},
{
label: "Error",
data: statsData.map((statData) => statData.stats.jobs?.errorJobs ?? 0),
backgroundColor: "#A13764",
},
{
label: "Unknown",
data: statsData.map((statData) => statData.stats.jobs?.unknownJobs ?? 0),
backgroundColor: "#6C757D",
},
];
};

const getPagesDataset = (statsData: GlobalStats[]) => {
return [
{
label: "Scraped",
data: statsData.map((statData) => statData.stats.pages.scrapedPages ?? 0),
backgroundColor: "#32C3A4",
},
{
label: "Missed",
data: statsData.map((statData) => statData.stats.pages.missedPages ?? 0),
backgroundColor: "#A13764",
},
];
};

const getItemsDataset = (statsData: GlobalStats[]) => {
const datasets = [
{
label: "Scraped",
data: statsData.map((statData) => statData.stats.itemsCount ?? 0),
backgroundColor: "#32C3A4",
},
];
return datasets;
};

const getRuntimeDataset = (statsData: GlobalStats[]) => {
return [
{
label: "Runtime (seconds)",
data: statsData.map((statData) => statData.stats.runtime ?? 0),
backgroundColor: "#32C3A4",
},
];
};

const getCoverageDataset = (statsData: GlobalStats[]) => {
return [
{
label: "Item coverage (percentage)",
data: statsData.map((statsData) => statsData.stats.coverage.totalItemsCoverage ?? 0),
backgroundColor: "#32C3A4",
},
];
};

const getSuccessRateDataset = (statsData: GlobalStats[]) => {
return [
{
label: "Success rate (percentage)",
data: statsData.map((statData) => statData.stats.successRate ?? 0),
backgroundColor: "#32C3A4",
},
];
};

const getStatusCodeDataset = (statsData: GlobalStats[]) => {
return [
{
label: "200",
data: statsData.map((statData) => statData.stats.statusCodes.status200 ?? 0),
backgroundColor: ["#32C3A4"],
},
{
label: "301",
data: statsData.map((statData) => statData.stats.statusCodes.status301 ?? 0),
backgroundColor: "#D1A34F",
},
{
label: "302",
data: statsData.map((statData) => statData.stats.statusCodes.status302 ?? 0),
backgroundColor: "#A13764",
},
{
label: "401",
data: statsData.map((statData) => statData.stats.statusCodes.status401 ?? 0),
backgroundColor: "#3C7BC6",
},
{
label: "403",
data: statsData.map((statData) => statData.stats.statusCodes.status403 ?? 0),
backgroundColor: "#7DC932",
},
{
label: "404",
data: statsData.map((statData) => statData.stats.statusCodes.status404 ?? 0),
backgroundColor: "#FE9F99",
},
{
label: "429",
data: statsData.map((statData) => statData.stats.statusCodes.status429 ?? 0),
backgroundColor: "#E7E255",
},
{
label: "500",
data: statsData.map((statData) => statData.stats.statusCodes.status500 ?? 0),
backgroundColor: "#6C757D",
},
];
};

const getLogsDataset = (statData: GlobalStats[]) => {
return [
{
label: "INFO",
data: statData.map((statData) => statData.stats.logs.infoLogs ?? 0),
backgroundColor: "#32C3A4",
},
{
label: "DEBUG",
data: statData.map((statData) => statData.stats.logs.debugLogs ?? 0),
backgroundColor: "#D1A34F",
},
{
label: "ERROR",
data: statData.map((statData) => statData.stats.logs.errorLogs ?? 0),
backgroundColor: "#A13764",
},
{
label: "WARNING",
data: statData.map((statData) => statData.stats.logs.warningLogs ?? 0),
backgroundColor: "#E7E255",
},
{
label: "CRITICAL",
data: statData.map((statData) => statData.stats.logs.criticalLogs ?? 0),
backgroundColor: "#6C757D",
},
];
};

interface ChartProps {
loadedStats: boolean;
stats: GlobalStats[] | SpidersJobsStats[];
statOption: StatType;
}

export class ChartsSection extends Component<ChartProps, unknown> {
datasetsGenerators: { [key in StatType]: (statsData: GlobalStats[]) => ChartDataset<"bar", number[]>[] } = {
[StatType.JOBS]: getJobsDataset,
[StatType.PAGES]: getPagesDataset,
[StatType.ITEMS]: getItemsDataset,
[StatType.RUNTIME]: getRuntimeDataset,
[StatType.COVERAGE]: getCoverageDataset,
[StatType.SUCCESS_RATE]: getSuccessRateDataset,
[StatType.STATUS_CODE]: getStatusCodeDataset,
[StatType.LOGS]: getLogsDataset,
};

render() {
const { loadedStats, stats, statOption } = this.props;

const labels: string[] = stats.map((stat) => stat.date.toLocaleDateString().slice(0, 10));

const datasets: ChartDataset<"bar", number[]>[] = this.datasetsGenerators[statOption](stats);

const data: ChartData<"bar", number[], string> = {
labels: labels,
datasets: datasets,
};

if (!loadedStats) {
return <Spinner />;
}

return (
<>
{stats.length === 0 ? (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
) : (
<Bar
options={{
responsive: true,
plugins: {
legend: {
position: "bottom" as const,
},
},
scales: {
x: {
stacked: true,
grid: {
display: false,
},
},
y: {
stacked: true,
min:
statOption === StatType.COVERAGE || statOption === StatType.SUCCESS_RATE
? 0
: undefined,
max:
statOption === StatType.COVERAGE || statOption === StatType.SUCCESS_RATE
? 100
: undefined,
},
},
}}
data={data}
/>
)}
</>
);
}
}
144 changes: 144 additions & 0 deletions estela-web/src/components/Stats/HeaderSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React, { Component } from "react";
import { DatePicker, Button, Divider, Layout, Tabs } from "antd";
import moment from "moment";
import Run from "../../assets/icons/run.svg";
import { ApiService, GlobalStats, SpidersJobsStats } from "../../services";
import { StatType } from "../../shared";
import { Row } from "antd";
import { ChartsSection } from "./ChartsSection";

const { RangePicker } = DatePicker;
const { Content } = Layout;

type RangeValue = Parameters<NonNullable<React.ComponentProps<typeof DatePicker.RangePicker>["onChange"]>>[0];

interface HeaderChartProps {
projectId: string;
spiderId?: string;
loadedStats: boolean;
stats: GlobalStats[] | SpidersJobsStats[];
onRefreshEventHandler: React.MouseEventHandler<HTMLElement> | undefined;
onChangeDateRangeHandler: ((values: RangeValue, formatString: [string, string]) => void) | undefined;
}

export class HeaderSection extends Component<HeaderChartProps, unknown> {
apiService = ApiService();

render() {
const { stats, loadedStats, onRefreshEventHandler, onChangeDateRangeHandler } = this.props;

const reversedStats = stats.slice().reverse();

return (
<>
<Row className="flow-root items-center justify-end space-x-4 space-x-reverse">
<RangePicker
onChange={onChangeDateRangeHandler}
defaultValue={[moment().subtract(7, "days").startOf("day").utc().local(), moment.utc().local()]}
ranges={{
Today: [moment().startOf("day"), moment().endOf("day")],
"Last 72h": [moment().subtract(3, "days").startOf("day"), moment().endOf("day")],
"Last 7 Days": [moment().subtract(7, "days").startOf("day"), moment().endOf("day")],
"Last 14 Days": [moment().subtract(14, "days").startOf("day"), moment().endOf("day")],
"Last 30 Days": [moment().subtract(30, "days").startOf("day"), moment().endOf("day")],
}}
format="YYYY-MM-DD"
className="flex float-right w-60 items-center rounded-lg font-medium stroke-white border-estela-blue-full hover:stroke-estela bg-estela-blue-low"
/>
<Button
icon={<Run className="mr-2" width={19} />}
className="flex float-right items-center rounded-3xl font-medium stroke-estela border-estela hover:stroke-estela bg-estela-blue-low text-estela hover:text-estela text-sm hover:border-estela"
onClick={onRefreshEventHandler}
>
Refresh
</Button>
</Row>
<Divider className="bg-estela-black-low mb-5" />
<Content className="flow-root">
<Tabs
className="w-full float-right text-estela-black-medium text-xs md:text-sm"
items={[
{
label: "Jobs",
key: StatType.JOBS,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.JOBS}
/>
),
},
{
label: "Pages",
key: StatType.PAGES,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.PAGES}
/>
),
},
{
label: "Items",
key: StatType.ITEMS,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.ITEMS}
/>
),
},
{
label: "Runtime",
key: StatType.RUNTIME,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.RUNTIME}
/>
),
},
{
label: "Job success rate",
key: StatType.SUCCESS_RATE,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.SUCCESS_RATE}
/>
),
},
{
label: "Status code",
key: StatType.STATUS_CODE,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.STATUS_CODE}
/>
),
},
{
label: "Logs",
key: StatType.LOGS,
children: (
<ChartsSection
stats={reversedStats}
loadedStats={loadedStats}
statOption={StatType.LOGS}
/>
),
},
]}
/>
</Content>
</>
);
}
}

0 comments on commit b1c9f57

Please sign in to comment.