diff --git a/django_project/frontend/src/components/ChartCard.tsx b/django_project/frontend/src/components/ChartCard.tsx index ef2cc5a6..67b4a9a3 100644 --- a/django_project/frontend/src/components/ChartCard.tsx +++ b/django_project/frontend/src/components/ChartCard.tsx @@ -75,7 +75,7 @@ const ChartCard: React.FC = ({ config, className }) => { const getChartComponent = () => { try { - return ; + return ; } catch (error) { console.error("Error processing data:", error); return ( diff --git a/django_project/frontend/src/components/DashboardCharts/CombinedCharts.tsx b/django_project/frontend/src/components/DashboardCharts/CombinedCharts.tsx index 7512e2bb..f6032a13 100644 --- a/django_project/frontend/src/components/DashboardCharts/CombinedCharts.tsx +++ b/django_project/frontend/src/components/DashboardCharts/CombinedCharts.tsx @@ -11,49 +11,49 @@ import 'chartjs-adapter-date-fns'; Chart.register(CategoryScale); interface Props { - analysis: Analysis; + analysisResults: any[]; } -export function BarChart({ analysis }: Props) { - const jsonData = analysis.results?.[0]; +// helper function for extracting analysis data for charts +const getAnalysisJsonData = (analysis: any, index: number) => { + const analysisData = analysis.analysis_results ?? analysis ?? []; - if (!jsonData || !jsonData.features || jsonData.features.length === 0) { + if (!analysisData?.results || analysisData.results.length === 0) { + return null; // Return null if no data is available + } + + return analysisData.results[index] ?? null; +}; + + +export function BarChart({ analysisResults }: Props) { + if (!analysisResults || analysisResults.length === 0) { return; } - const name1 = jsonData.features?.[0]?.properties?.Name; - const name2 = jsonData.features?.[1]?.properties?.Name || null; + let labels: number[] = []; + let datasets: any[] = []; - const labels: number[] = jsonData.features?.length - ? [jsonData.features?.[0]?.properties?.year, jsonData.features?.[jsonData.features.length - 1]?.properties?.year] - : []; + analysisResults.forEach((analysis, index) => { + const analysisData = analysis.analysis_results ?? analysis ?? []; - const dataBar1 = jsonData.features - .filter((feature: any) => feature?.properties?.Name === name1) - .map((feature: any) => feature?.properties?.[analysis.data.variable] || 0); + const jsonData = getAnalysisJsonData(analysis, 0); - let chartData: any = { - labels, - datasets: [ - { - label: name1, - data: dataBar1, - backgroundColor: "blue" - } - ], - }; + if (!jsonData || !jsonData.features || jsonData.features.length === 0) return; - if (name2 !== null && name1 !== name2) { - const dataBar2 = jsonData.features - .filter((feature: any) => feature?.properties?.Name === name2) - .map((feature: any) => feature?.properties?.[analysis.data.variable] || 0); + const name = jsonData.features[0]?.properties?.Name; + const data = jsonData.features.map((feature: any) => feature?.properties?.[analysisData?.data?.variable] || 0); - chartData.datasets.push({ - label: name2, - data: dataBar2, - backgroundColor: "red" + labels = jsonData.features.map((feature: any) => feature?.properties?.year); + + datasets.push({ + label: `${name} (Result ${index + 1})`, + data, + backgroundColor: index % 2 === 0 ? "blue" : "red" }); - } + }); + + const chartData = { labels, datasets }; const options: any = { responsive: true, @@ -76,46 +76,36 @@ export function BarChart({ analysis }: Props) { return ; } -export function LineChart({ analysis }: Props) { - const jsonData = analysis.results?.[1]; - - if (!jsonData || !jsonData.features || jsonData.features.length === 0) { +export function LineChart({ analysisResults }: Props) { + if (!analysisResults || analysisResults.length === 0) { return; } - const name1 = jsonData.features?.[0]?.properties?.Name; - const name2 = jsonData.features?.[1]?.properties?.Name || null; + let labels: number[] = []; + let datasets: any[] = []; - const labels: number[] = jsonData.features - .filter((feature: any) => feature?.properties?.Name === name1) - .map((feature: any) => feature?.properties?.date || ''); - const data1 = jsonData.features - .filter((feature: any) => feature?.properties?.Name === name1) - .map((feature: any) => feature?.properties?.[analysis.data.variable] || 0); + analysisResults.forEach((analysis, index) => { + const analysisData = analysis.analysis_results ?? analysis ?? []; - let chartData: any = { - labels, - datasets: [ - { - label: name1, - data: data1, - backgroundColor: "blue" - } - ], - }; + const jsonData = getAnalysisJsonData(analysis, 1); + + if (!jsonData || !jsonData.features || jsonData.features.length === 0) return; - if (name1 !== name2) { - const data2 = jsonData.features - .filter((feature: any) => feature?.properties?.Name === name2) - .map((feature: any) => feature?.properties?.[analysis.data.variable] || 0); + const name = jsonData.features[0]?.properties?.Name; + const data = jsonData.features.map((feature: any) => feature?.properties?.[analysisData?.data.variable] || 0); - chartData.datasets.push({ - label: name2, - data: data2, - backgroundColor: "red" + // Add labels only once, preserving unique dates + labels = Array.from(new Set([...labels, ...jsonData.features.map((feature: any) => feature?.properties?.date)])); + + + datasets.push({ + label: `${name} (Result ${index + 1})`, + data, + borderColor: index % 2 === 0 ? "blue" : "red", + fill: false, }); - } + }); const options: any = { responsive: true, @@ -147,25 +137,39 @@ export function LineChart({ analysis }: Props) { }, }; + const chartData = { labels, datasets }; return ; } -function SpatialBarChart({ analysis }: Props) { - const featureCollection: FeatureCollection | undefined = analysis.results; - if (!featureCollection || !featureCollection.features || featureCollection.features.length === 0) { - return; +function SpatialBarChart({ analysisResults }: { analysisResults: any[] }) { + if (!analysisResults || analysisResults.length === 0) { + return null; } - const labels: string[] = featureCollection.features.map((feature) => feature?.properties?.['Name'] || 'Unknown'); - let chartData: any = { + let mergedFeatures: any[] = []; + analysisResults.forEach((analysis) => { + const results = analysis?.analysis_results?.results ?? analysis?.results; + if (results?.features) { + mergedFeatures = [...mergedFeatures, ...results.features]; + } + }); + + if (mergedFeatures.length === 0) { + return null; + } + + const labels = mergedFeatures.map((feature) => feature?.properties?.["Name"] || "Unknown"); + + // Aggregate data for the bar chart + const chartData: any = { labels, datasets: [ { - label: '% difference to reference area', - data: featureCollection.features.map((feature) => feature?.properties?.["mean"] || 0), - backgroundColor: "blue" - } + label: "% difference to reference area", + data: mergedFeatures.map((feature) => feature?.properties?.["mean"] || 0), + backgroundColor: "blue", + }, ], }; @@ -178,87 +182,111 @@ function SpatialBarChart({ analysis }: Props) { }, title: { display: true, + text: "Spatial Analysis Results", }, subtitle: { display: true, - text: 'Feature (labeled by', - position: 'bottom' - } + text: "Feature (labeled by Name)", + position: "bottom", + }, }, scales: { y: { beginAtZero: true, title: { display: true, - text: 'mean' - } + text: "Mean", + }, }, - } + }, }; - return ; + return ; } -export function RenderBaseline({ analysis }: Props) { - if (!analysis.results?.features) { - return; - } - const keys = Object.keys(analysis.results?.columns || {}); + +export function RenderBaseline({ analysisResults }: Props) { return ( - - - - - - {keys.map((column: string) => )} - - - - {analysis.results.features.map((feature: any, index: number) => { - const properties = feature?.properties || {}; - return ( - - - {keys.map((column: string) => ( - - ))} - - ); - })} - -
Name{column}
{properties.Name || 'N/A'}{properties[column] || 'N/A'}
+ + {analysisResults.map((analysis, index) => { + const results = analysis?.analysis_results?.results ?? analysis?.results; + if (!results?.features) { + return; + } + + const keys = Object.keys(results?.columns || {}); + return ( + + + + + + {keys.map((column: string) => )} + + + + {results?.features.map((feature: any, idx: number) => { + const properties = feature?.properties || {}; + return ( + + + {keys.map((column: string) => ( + + ))} + + ); + })} + +
Name{column}
{properties.Name || 'N/A'}{properties[column] || 'N/A'}
+
+ ); + })}
); } -export function RenderTemporal({ analysis }: Props) { - const jsonDataLine = analysis.results?.[1]; - const jsonDataBar = analysis.results?.[0]; - const hasLineData = jsonDataLine?.features?.length > 0; - const hasBarData = jsonDataBar?.features?.length > 0; +export function RenderTemporal({ analysisResults }: Props) { + if (!analysisResults || analysisResults.length === 0) { + return No temporal analysis data available.; + } + + const hasBarData = analysisResults.some((analysis) => + (analysis.analysis_results?.results?.[0]?.features?.length ?? 0) > 0 || + (analysis.results?.[0]?.features?.length ?? 0) > 0 + ); + + const hasLineData = analysisResults.some((analysis) => + (analysis.analysis_results?.results?.[1]?.features?.length ?? 0) > 0 || + (analysis.results?.[1]?.features?.length ?? 0) > 0 + ); + const charts = []; + console.log('hasbar data ',hasBarData) + if(hasLineData && hasBarData){ charts.push( - + ); charts.push( - + ); }else if (hasLineData) { charts.push( - + ); }else if (hasBarData) { charts.push( - + + + ); } @@ -275,24 +303,42 @@ export function RenderTemporal({ analysis }: Props) { -export function RenderSpatial({ analysis }: Props) { +export function RenderSpatial({ analysisResults }: Props) { return ( - Relative % difference in {analysis.data.variable} between your reference area and selected camp/s: - + {analysisResults.map((analysis, index) => ( + + + Relative % difference in {analysis?.data?.variable} between your reference area and selected camp/s: + + {/* Pass analysis as an array */} + + + ))} ); } -export function RenderResult({ analysis }: Props) { - switch (analysis?.data?.analysisType) { + + +export function RenderResult({ analysisResults }: Props) { + if (!analysisResults || analysisResults.length === 0) { + return No analysis results available.; + } + + const analysisType = (analysisResults[0]?.analysis_results?.data?.analysisType)? analysisResults[0]?.analysis_results?.data?.analysisType: analysisResults[0]?.data?.analysisType; + + + + switch (analysisType) { case "Baseline": - return ; + return ; case "Temporal": - return ; + return ; case "Spatial": - return ; + return ; default: - return No analysis type selected.; + return Unknown analysis type.; } } + diff --git a/django_project/frontend/src/pages/AnalysisResults/index.tsx b/django_project/frontend/src/pages/AnalysisResults/index.tsx index 6acc867a..19148b08 100644 --- a/django_project/frontend/src/pages/AnalysisResults/index.tsx +++ b/django_project/frontend/src/pages/AnalysisResults/index.tsx @@ -129,7 +129,6 @@ export default function AnalysisResults() { dispatch(fetchAnalysis()); } }, [analysisDeleted]); - const getAnalysisSummary = (analysis: AnalysisData): AnalysisSummary => { const { analysis_results } = analysis || {}; @@ -182,6 +181,31 @@ export default function AnalysisResults() { }; const handleCreateDashboardClick = () => { + + // Filter analysis data based on selected analysis IDs + const matchedAnalysis = analysisData.filter((item: { id: any; }) => selectedAnalysis.includes(item.id)); + + // Extract analysis types from the matched analysis objects + const analysisTypes = matchedAnalysis.map((item: { analysis_results: { data: { analysisType: any; }; }; }) => item.analysis_results.data?.analysisType); + + // Check if all analysis types are the same + const allSameType = analysisTypes.every((type: any) => type === analysisTypes[0]); + + if (!allSameType) { + toast({ + title: "Cannot create dashboard.", + description: `Can only create a dashboard from analysis results with the same analysis type! Current selected results have: ${analysisTypes.join(", ")}`, + status: "warning", + duration: 5000, + isClosable: true, + position: "top-right", + containerStyle: { + backgroundColor: "#00634b", + color: "white", + }, + }); + return; + } // Open the Create Dashboard modal setCreateDashboard(true); };