I've made a server.js
in my npx cra
project that's connecting to atlas cluster and formatting the received data using a schema.
The data is about solar generation data I've downloaded from kaggle and uploaded to the solar collection using mongodb compass.
I ran into a problem when I tried to fetch the data and visualize it using graphs.
Using a graph component it keeps showing a single document regardless of date range.
Here's my server.js:
const express = require('express');const mongoose = require('mongoose');const cors = require('cors');require('dotenv').config();const app = express();app.use(cors());app.use(express.json());mongoose.connect(process.env.MONGODB_URI, { dbName: 'solargenerationdata'}) .then(() => console.log('Connected to MongoDB')) .catch((err) => console.error('Failed to connect to MongoDB:', err));const GenerationDataSchema = new mongoose.Schema({ _id: mongoose.Schema.Types.ObjectId, DATE_TIME: String, PLANT_ID: String, DC_POWER: Number, AC_POWER: Number, DAILY_YIELD: Number, TOTAL_YIELD: Number,});const GenerationDataModel = mongoose.model('solar', GenerationDataSchema, 'solar');// Modified API endpointsapp.get('/api/generation_data/summary', async (req, res) => { try { const startDate = req.query.startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); const endDate = req.query.endDate || new Date().toISOString(); const data = await GenerationDataModel.aggregate([ { $match: { DATE_TIME: { $gte: startDate, $lte: endDate } } }, { $group: { _id: { date: { $substr: ["$DATE_TIME", 0, 10] }, plantId: "$PLANT_ID" }, avgDcPower: { $avg: "$DC_POWER" }, avgAcPower: { $avg: "$AC_POWER" }, totalDailyYield: { $max: "$DAILY_YIELD" }, maxTotalYield: { $max: "$TOTAL_YIELD" } } }, { $sort: { "_id.date": 1 } } ]); console.log('Summary Data:', data); // Log the fetched data res.status(200).json(data); } catch (error) { res.status(500).json({ error: 'Server Error' }); }});app.get('/api/generation_data/latest', async (req, res) => { try { const latestData = await GenerationDataModel.findOne() .sort({ DATE_TIME: -1 }); console.log('Latest Data:', latestData); // Log the fetched data res.status(200).json(latestData); } catch (error) { res.status(500).json({ error: 'Server Error' }); }});const PORT = 5000;app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`);});
and graphcomponent.js
import { useEffect, useState } from 'react';import { CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';const GraphComponent = () => { const [data, setData] = useState([]); const [latestData, setLatestData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [dateRange, setDateRange] = useState({ from: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), to: new Date(), }); useEffect(() => { const fetchData = async () => { try { setLoading(true); setError(null); const params = new URLSearchParams({ startDate: dateRange.from.toISOString(), endDate: dateRange.to.toISOString(), }); // Fetch summary data const summaryResponse = await fetch(`http://localhost:5000/api/generation_data/summary?${params}`); if (!summaryResponse.ok) { throw new Error(`HTTP error! status: ${summaryResponse.status}`); } const summaryData = await summaryResponse.json(); // Process summary data const formattedData = summaryData.map(item => ({ date: new Date(item.DATE_TIME).toLocaleDateString(), plantId: item.PLANT_ID, dcPower: Number(item.DC_POWER) || 0, acPower: Number(item.AC_POWER) || 0, dailyYield: Number(item.DAILY_YIELD) || 0, totalYield: Number(item.TOTAL_YIELD) || 0 })); // Group by date and calculate averages const groupedData = formattedData.reduce((acc, curr) => { const existingEntry = acc.find(item => item.date === curr.date); if (existingEntry) { existingEntry.dcPower += curr.dcPower; existingEntry.acPower += curr.acPower; existingEntry.dailyYield += curr.dailyYield; existingEntry.count += 1; } else { acc.push({ date: curr.date, dcPower: curr.dcPower, acPower: curr.acPower, dailyYield: curr.dailyYield, count: 1 }); } return acc; }, []); // Calculate final averages const finalData = groupedData.map(item => ({ date: item.date, avgDcPower: Number((item.dcPower / item.count).toFixed(2)), avgAcPower: Number((item.acPower / item.count).toFixed(2)), totalDailyYield: Number((item.dailyYield / item.count).toFixed(2)) })).sort((a, b) => new Date(a.date) - new Date(b.date)); // Fetch latest data const latestResponse = await fetch('http://localhost:5000/api/generation_data/latest'); if (!latestResponse.ok) { throw new Error(`HTTP error! status: ${latestResponse.status}`); } const latestDataResponse = await latestResponse.json(); setData(finalData); setLatestData(latestDataResponse); setLoading(false); } catch (error) { console.error('Error in fetchData:', error); setError(`Error fetching data: ${error.message}`); setLoading(false); } }; fetchData(); }, [dateRange]); const handleDateChange = (event) => { const { name, value } = event.target; setDateRange(prev => ({ ...prev, [name]: new Date(value) })); }; if (loading) { return (<div className="flex items-center justify-center h-64"><div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div><span className="ml-2">Loading data...</span></div> ); } return (<div className="space-y-6 p-4"> {error && (<div className="bg-red-50 border-l-4 border-red-400 p-4 rounded"><div className="flex"><div className="ml-3"><h3 className="text-sm font-medium text-red-800">Error</h3><div className="mt-2 text-sm text-red-700">{error}</div></div></div></div> )} {/* Date Range Selection */}<div className="bg-white p-4 rounded-lg shadow"><h2 className="text-xl font-semibold mb-4">Select Date Range</h2><div className="flex gap-4"><div><label className="block text-sm font-medium text-gray-700">From</label><input type="date" name="from" value={dateRange.from.toISOString().split('T')[0]} onChange={handleDateChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" /></div><div><label className="block text-sm font-medium text-gray-700">To</label><input type="date" name="to" value={dateRange.to.toISOString().split('T')[0]} onChange={handleDateChange} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" /></div></div></div> {/* Current Metrics */} {latestData && (<div className="bg-white p-4 rounded-lg shadow"><h2 className="text-xl font-semibold mb-4">Current Solar Generation Metrics</h2><div className="grid grid-cols-1 md:grid-cols-3 gap-4"><div className="p-4 bg-gray-100 rounded-lg"><h3 className="text-sm font-medium text-gray-500">Plant ID</h3><p className="text-2xl font-bold">{latestData.PLANT_ID || 'N/A'}</p></div><div className="p-4 bg-gray-100 rounded-lg"><h3 className="text-sm font-medium text-gray-500">Daily Yield</h3><p className="text-2xl font-bold">{latestData.DAILY_YIELD?.toFixed(2) || '0'} kWh</p></div><div className="p-4 bg-gray-100 rounded-lg"><h3 className="text-sm font-medium text-gray-500">Total Yield</h3><p className="text-2xl font-bold">{latestData.TOTAL_YIELD?.toFixed(2) || '0'} kWh</p></div></div></div> )} {/* Power Generation Trends */} {data.length > 0 ? (<div className="bg-white p-4 rounded-lg shadow"><h2 className="text-xl font-semibold mb-4">Power Generation Trends</h2><div className="h-[400px]"><ResponsiveContainer width="100%" height="100%"><LineChart data={data}><CartesianGrid strokeDasharray="3 3" /><XAxis dataKey="date" tick={{ fontSize: 12 }} angle={-45} textAnchor="end" /><YAxis /><Tooltip /><Legend /><Line type="monotone" dataKey="avgDcPower" stroke="#8884d8" name="Avg DC Power (kW)" dot={false} /><Line type="monotone" dataKey="avgAcPower" stroke="#82ca9d" name="Avg AC Power (kW)" dot={false} /><Line type="monotone" dataKey="totalDailyYield" stroke="#ffc658" name="Daily Yield (kWh)" dot={false} /></LineChart></ResponsiveContainer></div></div> ) : (<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4"><div className="flex"><div className="ml-3"><h3 className="text-sm font-medium text-yellow-800">No Data Available</h3><div className="mt-2 text-sm text-yellow-700"> No generation data available for the selected date range.</div></div></div></div> )}</div> );};export default GraphComponent;
thanks in advance for your help !