Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish implementing the chart and tables for deployment frequency #5

Merged
merged 3 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions components/dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
CardTitle,
} from "@/components/ui/card"

import { IsolatedDeploymentFrequencyChart } from '@/components/deployment-frequency/iso-chart'
import { DeploymentFrequencyChart } from '@/components/deployment-frequency/chart'
import { LeadTimeForChangeChart } from '@/components/lead-time-for-change/chart'
import { IsolatedChangeFailureRateChart } from '@/components/change-failure-rate/iso-chart'
import { IsolatedMeanTimeToRecoveryChart } from '@/components/mean-time-to-recovery/iso-chart'
Expand Down Expand Up @@ -134,14 +134,14 @@ export function Dashboard({ data, appList }) {
</TabsList>
<TabsContent value="dora-df" className="p-6 mt-8">
<div className="h-64">
<IsolatedDeploymentFrequencyChart data={dataDeploymentFrequency} />
<DeploymentFrequencyChart dateRange={activeDateRange} appName={activeApp} />
</div>
<div className="mt-8">
<h2 className="flex items-center gap-2 mb-4 font-semibold dark:text-white">
<TableIcon />
Deployments
</h2>
<DeploymentFrequencyTable />
<DeploymentFrequencyTable dateRange={activeDateRange} appName={activeApp} />
</div>
</TabsContent>
<TabsContent value="dora-ltfc" className="p-6 mt-8">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
'use client'

import { useState } from 'react'
import { useTheme } from "next-themes"
import { format } from 'date-fns'
import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ComposedChart, Area, Line, ReferenceLine } from 'recharts'
import { DeploymentFrequencyTooltip } from './tooltip'

const dateFormatter = date => {
return format(new Date(date), "MMM d")
}
import useDeploymentFrequencyData from './deploymentFrequency';
import { dateFormatter, dayFormatter } from '@/lib/date-funcs';

const calculateMean = data => {
if (data.length < 1) {
Expand All @@ -17,7 +13,38 @@ const calculateMean = data => {
return data.reduce((prev, current) => prev + current) / data.length
}

export function IsolatedDeploymentFrequencyChart({ data }) {
function deploymentsPerDay(data) {
// Object to store the count of deployments per day
const deploymentsCountPerDay = {};

// Loop through each item in the data array
data.forEach(item => {
// Convert timestamp to Date object and extract the date
const date = new Date(item.timestamp * 1000);
// Format the date to YYYY-MM-DD
const formattedDate = date.toLocaleDateString().split('T')[0];

// Increment the count of deployments for the corresponding date
if (deploymentsCountPerDay[formattedDate]) {
deploymentsCountPerDay[formattedDate]++;
} else {
deploymentsCountPerDay[formattedDate] = 1;
}
});

// Convert the deployments count per day object into an array of objects
const result = Object.keys(deploymentsCountPerDay).map(date => ({
// Convert date back to epoch format and assign to day_epoch property
day_epoch: new Date(date).getTime() / 1000,
// Assign the count of deployments to the count property
count: deploymentsCountPerDay[date]
}));

// Return the result
return result;
}

export function DeploymentFrequencyChart({ dateRange, appName }) {

const { resolvedTheme } = useTheme()
const animationDuration = 1000
Expand All @@ -36,27 +63,37 @@ export function IsolatedDeploymentFrequencyChart({ data }) {
const strokeRollingAverage = '#3b82f6' // Blue 500
const strokeGoal = '#f59e0b' // Amber 500

const { dfData, loading } = useDeploymentFrequencyData(appName, dateRange);
console.log('Chart dfData: ', dfData)

if (loading) {
return <div>Loading...</div>; // Render loading state while data is being fetched
}

var countPerDay = deploymentsPerDay(dfData);
console.log('Count per day', countPerDay)

// Calculate the mean
const averages = data.map(element => {
return element.rollingAverage
const averages = dfData.map(element => {
return element.count
})

const chartMean = calculateMean(averages)

// Reports
const [reportDeploymentFrequencyData, setReportDeploymentFrequencyData] = useState(null)
const [showReportDeploymentFrequencyData, setShowReportDeploymentFrequencyData] = useState(false)
// // Reports
// const [reportDeploymentFrequencyData, setReportDeploymentFrequencyData] = useState(null)
// const [showReportDeploymentFrequencyData, setShowReportDeploymentFrequencyData] = useState(false)

function handleChartClick(event) {
setReportDeploymentFrequencyData(event)
setShowReportDeploymentFrequencyData(true)
}
// function handleChartClick(event) {
// setReportDeploymentFrequencyData(event)
// setShowReportDeploymentFrequencyData(true)
// }

// Anomaly detection
const showAnomalyWarning = data.some((day) => {
if (day.rollingAverage < day.expectedRange[0] || day.rollingAverage > day.expectedRange[1]) { return true }
return false
})
// // Anomaly detection
// const showAnomalyWarning = dfData.some((day) => {
// if (day.rollingAverage < day.expectedRange[0] || day.rollingAverage > day.expectedRange[1]) { return true }
// return false
// })

const customAnomalyLabel = props => {
return (
Expand All @@ -70,20 +107,20 @@ export function IsolatedDeploymentFrequencyChart({ data }) {
return (
<>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={data} margin={{ top: 0, left: 0, right: 4, bottom: 0 }} onClick={handleChartClick}>
<ComposedChart data={countPerDay} margin={{ top: 0, left: 0, right: 4, bottom: 0 }} /*onClick={handleChartClick}*/>
<CartesianGrid vertical={false} stroke={resolvedTheme === 'dark' ? strokeGridDark : strokeGrid} />
<XAxis style={{ fontSize: '0.75rem' }} dataKey="date" axisLine={false} tickLine={false} tickFormatter={dateFormatter} />
<XAxis style={{ fontSize: '0.75rem' }} dataKey="day_epoch" axisLine={false} tickLine={false} tickFormatter={dateFormatter} />
<YAxis style={{ fontSize: '0.75rem' }} domain={[0, 20]} axisLine={false} tickLine={false} />
<Tooltip content={<DeploymentFrequencyTooltip />} cursor={{ stroke: strokeCursor }} />
<Area type="monotone" dataKey="expectedRange" activeDot={resolvedTheme === 'dark' ? { stroke: strokeActiveDotDark } : { stroke: strokeActiveDot }} fill={resolvedTheme === 'dark' ? fillRangeDark : fillRange} stroke={strokeRange} strokeWidth={0} strokeDasharray="4 4" animationDuration={animationDuration} />
<Line type="monotone" dataKey="rollingAverage" dot={false} activeDot={resolvedTheme === 'dark' ? { stroke: strokeActiveDotDark } : { stroke: strokeActiveDot }} stroke={strokeRollingAverage} strokeWidth={3} strokeLinecap="round" animationDuration={animationDuration} />
<Line type="monotone" dataKey="count" dot={false} activeDot={resolvedTheme === 'dark' ? { stroke: strokeActiveDotDark } : { stroke: strokeActiveDot }} stroke={strokeRollingAverage} strokeWidth={3} strokeLinecap="round" animationDuration={animationDuration} />
<Line type="monotone" dataKey="goal" dot={false} activeDot={resolvedTheme === 'dark' ? { stroke: strokeActiveDotDark } : { stroke: strokeActiveDot }} stroke={strokeGoal} strokeWidth={2} strokeDasharray="4 4" strokeLinecap="round" isAnimationActive={false} />

{data.map((day, index) => (
{/* {dfData.map((day, index) => (
day.rollingAverage < day.expectedRange[0] || day.rollingAverage > day.expectedRange[1] && (
<ReferenceLine className="anomaly-reference-line" key={index} x={day.date} stroke={strokeAnomaly} strokeWidth={1} label={customAnomalyLabel} />
)
))}
))} */}
</ComposedChart>
</ResponsiveContainer>
{/* <DeploymentFrequencyReport reportDeploymentFrequencyData={reportDeploymentFrequencyData} showReportDeploymentFrequencyData={showReportDeploymentFrequencyData} setShowReportDeploymentFrequencyData={setShowReportDeploymentFrequencyData} /> */}
Expand Down
28 changes: 28 additions & 0 deletions components/deployment-frequency/deploymentFrequency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// components/deploymentFrequency.js
import { useState, useEffect } from 'react';
import { getDaysBetweenDates } from '@/components/date-range-selector'

export default function useDeploymentFrequencyData(appName, dateRange) {
const [dfData, setDfData] = useState([]);
const [loading, setLoading] = useState(true); // Add loading state

useEffect(() => {
const fetchData = async () => {
try {
const req = `${process.env.NEXT_PUBLIC_PELORUS_API_URL}/sdp/deployment_frequency/${appName}/data?range=${getDaysBetweenDates(dateRange)}d&start=${dateRange.to.getTime() / 1000}`;
const response = await fetch(req);
const data = await response.json();
const sortedData = data.sort((d1, d2) => (d1.timestamp > d2.timestamp) ? 1 : (d1.timestamp < d2.timestamp) ? -1 : 0);
setDfData(sortedData);
} catch (error) {
console.error('Error fetching deployment frequency data:', error);
} finally {
setLoading(false); // Set loading to false regardless of success or failure
}
};

fetchData();
}, [appName, dateRange]);

return { dfData, loading }; // Return loading state along with dfData
}
2 changes: 1 addition & 1 deletion components/deployment-frequency/tab-trigger.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function DeploymentFrequencyTabTrigger({ dateRange, data, appName }) {
if (isLoading) return <p>Loading...</p>
if (!response) return <p>No cfr data!</p>

const chartMean = response.df
const chartMean = response.df / getDaysBetweenDates(dateRange)
const percentChange = Math.round((response.df / response.last) * 100)

// Anomaly detection
Expand Down
Loading
Loading