// components/uploadDataModal.tsx import { useState, useEffect } from "react"; import { Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from "@nextui-org/react"; import { Accordion, AccordionItem } from "@nextui-org/react"; import { AzureKeyCredential, DocumentAnalysisClient } from "@azure/ai-form-recognizer"; import { useRouter } from 'next/navigation'; import { ElectricityDataPoint, NaturalGasDataPoint } from "../lib/useBuildingData"; interface UploadDataModalProps { isOpen: boolean; onClose: () => void; buildingid: string; updateBuilding: (newData: any) => void; } const EMISSIONS_FACTOR = 0.5; const key = process.env.NEXT_PUBLIC_FORM_RECOGNIZER_KEY; const endpoint = process.env.NEXT_PUBLIC_FORM_RECOGNIZER_ENDPOINT; export function UploadDataModal({ isOpen, onClose, buildingid, updateBuilding }: UploadDataModalProps) { const [isSubmitted, setIsSubmitted] = useState(false); const [gasFile, setGasFile] = useState(null); const [electricityFile, setElectricityFile] = useState(null); const [gasFileUrl, setGasFileUrl] = useState(null); const [electricityFileUrl, setElectricityFileUrl] = useState(null); const [extractionStatus, setExtractionStatus] = useState<'idle' | 'loading' | 'complete'>('idle'); const [aiExtractionStatus, setAiExtractionStatus] = useState<'idle' | 'loading' | 'complete'>('idle'); const [dataPreview, setDataPreview] = useState(null); const router = useRouter(); const handleFileUpload = (type: 'gas' | 'electricity', file: File) => { if (type === 'gas') { setGasFile(file); setGasFileUrl(URL.createObjectURL(file)); } else if (type === 'electricity') { setElectricityFile(file); setElectricityFileUrl(URL.createObjectURL(file)); } setIsSubmitted(true); }; useEffect(() => { return () => { if (gasFileUrl) URL.revokeObjectURL(gasFileUrl); if (electricityFileUrl) URL.revokeObjectURL(electricityFileUrl); }; }, [gasFileUrl, electricityFileUrl]); const extractDataFromPDF = async (file: File, type: 'gas' | 'electricity') => { const client = new DocumentAnalysisClient(endpoint!, new AzureKeyCredential(key!)); const arrayBuffer = await file.arrayBuffer(); const poller = await client.beginAnalyzeDocument("prebuilt-document", arrayBuffer); const { keyValuePairs } = await poller.pollUntilDone(); if (!keyValuePairs) return []; const dataPoints: (ElectricityDataPoint | NaturalGasDataPoint)[] = []; let extractedDate: Date | null = null; const monthMap: { [key: string]: number } = { 'jan': 0, 'january': 0, 'feb': 1, 'february': 1, 'mar': 2, 'march': 2, 'apr': 3, 'april': 3, 'may': 4, 'jun': 5, 'june': 5, 'jul': 6, 'july': 6, 'aug': 7, 'august': 7, 'sep': 8, 'september': 8, 'oct': 9, 'october': 9, 'nov': 10, 'november': 10, 'dec': 11, 'december': 11 }; for (const { key, value } of keyValuePairs) { console.log("KEY:", key.content, "VALUE:", value?.content); if (!value) continue; const keyLower = key.content.toLowerCase(); const valueLower = value.content.toLowerCase(); // Extract date information if (keyLower.includes('date') || keyLower.includes('period')) { console.log("DATE IDENTIFIED:", valueLower); const dateMatch = valueLower.match(/(\d{1,2})\s*(?:st|nd|rd|th)?\s*(?:of)?\s*([a-z]+)?\s*(\d{4})?/i); console.log("DATE MATCH:", dateMatch); if (dateMatch) { const day = 1; // Always assume 1st of the month const month = dateMatch[2] ? monthMap[dateMatch[2].toLowerCase()] : new Date().getMonth(); const year = dateMatch[3] ? parseInt(dateMatch[3]) : new Date().getFullYear(); if (year >= 1900 && year <= 2100) { extractedDate = new Date(year, month, day); } } } if (type === 'electricity' && keyLower.includes('kwh')) { const kwh = parseFloat(value.content || '0'); if (kwh !== 0) { const timestamp = extractedDate || new Date(); timestamp.setHours(0, 0, 0, 0); // Set to midnight const existingDataIndex = dataPoints.findIndex(point => point.timestamp.seconds === timestamp.getTime() / 1000 ); if (existingDataIndex === -1) { dataPoints.push({ timestamp: { seconds: timestamp.getTime() / 1000, nanoseconds: 0 }, kwh: kwh, emissions: kwh * EMISSIONS_FACTOR / 1000, }); } else { dataPoints[existingDataIndex] = { ...dataPoints[existingDataIndex], kwh: kwh, emissions: kwh * EMISSIONS_FACTOR / 1000, }; } } } else if (type === 'gas' && keyLower.includes('therm')) { const therms = parseFloat(value.content || '0'); if (therms !== 0) { const timestamp = extractedDate || new Date(); timestamp.setHours(0, 0, 0, 0); // Set to midnight const existingDataIndex = dataPoints.findIndex(point => point.timestamp.seconds === timestamp.getTime() / 1000 ); if (existingDataIndex === -1) { dataPoints.push({ timestamp: { seconds: timestamp.getTime() / 1000, nanoseconds: 0 }, therms: therms, emissions: therms * 5.3 / 1000, // approx CO2 emissions for natural gas (5.3 kg CO2 per therm, measured in tons) }); } else { dataPoints[existingDataIndex] = { ...dataPoints[existingDataIndex], therms: therms, emissions: therms * 5.3 / 1000, }; } } } } return dataPoints; }; const handleExtraction = async () => { setExtractionStatus('loading'); try { let newData: any = {}; if (gasFile) { const gasData = await extractDataFromPDF(gasFile, 'gas'); console.log("Gas data:"); gasData.forEach(dataPoint => { console.log("Date:", new Date(dataPoint.timestamp.seconds * 1000).toLocaleDateString(), "Therms:", (dataPoint as NaturalGasDataPoint).therms); }); newData.naturalGasUsage = gasData; } if (electricityFile) { const electricityData = await extractDataFromPDF(electricityFile, 'electricity'); console.log("Electricity data:"); electricityData.forEach(dataPoint => { console.log("Date:", new Date(dataPoint.timestamp.seconds * 1000).toLocaleDateString(), "kWh:", (dataPoint as ElectricityDataPoint).kwh); }); newData.electricityUsage = electricityData; } setDataPreview(newData); setExtractionStatus('complete'); // Update the building data updateBuilding(newData); } catch (error) { console.error("Error during extraction:", error); setExtractionStatus('idle'); } }; const handleAIExtraction = async () => { setAiExtractionStatus('loading'); try { let newData: any = {}; if (gasFile) { const gasData = await extractDataUsingAI(gasFile, 'gas'); newData.naturalGasUsage = gasData; } if (electricityFile) { const electricityData = await extractDataUsingAI(electricityFile, 'electricity'); newData.electricityUsage = electricityData; } setDataPreview(newData); setAiExtractionStatus('complete'); // Update the building data updateBuilding(newData); } catch (error) { console.error("Error during AI extraction:", error); setAiExtractionStatus('idle'); } }; const extractDataUsingAI = async (file: File, type: 'gas' | 'electricity') => { // Step 1: Convert PDF to image const formData = new FormData(); formData.append('pdf', file, file.name); formData.append('type', type); const pdfToImageResponse = await fetch('/api/pdf-to-image', { method: 'POST', body: formData, }); if (!pdfToImageResponse.ok) { throw new Error('Failed to convert PDF to image'); } const { response } = await pdfToImageResponse.json(); console.log("PDF TO IMAGE RESPONSE", response); // Parse the JSON response const parsedData: string = response.response; //Trim the string to remove the "anything before first {" and "and after last }" const trimmedData = parsedData.replace(/^[^{]*|[^}]*$/g, ''); const parsedTrimmedData = JSON.parse(trimmedData); console.log("PARSED TRIMMED DATA", parsedTrimmedData); // Convert the parsed data to the format expected by the application return parsedTrimmedData.dataPoints.map((point: any) => ({ timestamp: { seconds: new Date(point.date).getTime() / 1000, nanoseconds: 0 }, [type === 'gas' ? 'therms' : 'kwh']: point.usage, emissions: point.usage * (type === 'gas' ? 5.3 : EMISSIONS_FACTOR) / 1000, })); }; return ( {!isSubmitted ? ( <> Upload New Data
document.getElementById('gas-upload')?.click()} onDragOver={(e) => e.preventDefault()} onDrop={(e) => { e.preventDefault(); const file = e.dataTransfer.files[0]; if (file) handleFileUpload('gas', file); }} onKeyDown={(e) => { if ((e.key === 'Enter' || e.key === ' ') && gasFile) { e.preventDefault(); } }} >

Click or drag to upload gas bill PDF

{ const file = e.target.files?.[0]; if (file) handleFileUpload('gas', file); }} />
document.getElementById('electricity-upload')?.click()} onDragOver={(e) => e.preventDefault()} onDrop={(e) => { e.preventDefault(); const file = e.dataTransfer.files[0]; if (file) handleFileUpload('electricity', file); }} onKeyDown={(e) => { if ((e.key === 'Enter' || e.key === ' ') && electricityFile) { e.preventDefault(); } }} >

Click or drag to upload electricity bill PDF

{ const file = e.target.files?.[0]; if (file) handleFileUpload('electricity', file); }} />
) : ( <> Data Uploaded

Your file has been successfully uploaded! Please wait while we extract the data.

{(gasFile || electricityFile) && ( {gasFile && gasFileUrl && (

Gas Bill:

Name: {gasFile.name}

Type: {gasFile.type}

Size: {(gasFile.size / 1024).toFixed(2)} KB

)} {electricityFile && electricityFileUrl && (

Electricity Bill:

Name: {electricityFile.name}

Type: {electricityFile.type}

Size: {(electricityFile.size / 1024).toFixed(2)} KB

)}
{extractionStatus === 'idle' && aiExtractionStatus === 'idle' && (
)} {extractionStatus === 'loading' &&

Extracting data using Form Recognizer...

} {aiExtractionStatus === 'loading' &&

Extracting data using AI...

} {extractionStatus === 'complete' &&

Form Recognizer extraction complete!

} {aiExtractionStatus === 'complete' &&

AI-powered extraction complete!

}
{dataPreview ? (
{JSON.stringify(dataPreview, null, 2)}
) : (

No data available. Please complete extraction first.

)}
)}
)}
); }