Mirror of github.com/Abhisingh18/Rag-Model
Browse files- .gitignore +40 -0
- API_KEY_FIX.md +80 -0
- APPLICATION_RUNNING.md +196 -0
- BROWSER_FIX.md +38 -0
- HOW_IT_WORKS.md +541 -0
- HUGGINGFACE_SETUP.md +37 -0
- MONGODB_SETUP.md +83 -0
- README.md +245 -0
- SETUP_GUIDE.md +98 -0
- backend/.env.example +17 -0
- backend/.gitignore +7 -0
- backend/models/Document.js +53 -0
- backend/package.json +38 -0
- backend/routes/analysis.js +137 -0
- backend/routes/upload.js +221 -0
- backend/server.js +52 -0
- backend/services/analysisService.js +219 -0
- backend/services/documentParser.js +143 -0
- backend/services/huggingfaceService.js +169 -0
- backend/services/ragEngine.js +98 -0
- backend/services/reportGenerator.js +262 -0
- backend/test-direct-api.js +61 -0
- backend/test-gemini.js +38 -0
- frontend/index.html +14 -0
- frontend/package.json +26 -0
- frontend/src/App.jsx +139 -0
- frontend/src/components/AnalysisDashboard.jsx +841 -0
- frontend/src/components/FileUpload.jsx +270 -0
- frontend/src/components/ReportPreview.jsx +134 -0
- frontend/src/index.css +450 -0
- frontend/src/main.jsx +10 -0
- frontend/vite.config.js +15 -0
- sample_training_data.csv +51 -0
- test_data_1_recent.csv +36 -0
- test_data_2_detailed.csv +46 -0
.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dependencies
|
| 2 |
+
node_modules/
|
| 3 |
+
package-lock.json
|
| 4 |
+
|
| 5 |
+
# Environment variables
|
| 6 |
+
.env
|
| 7 |
+
.env.local
|
| 8 |
+
.env.development.local
|
| 9 |
+
.env.test.local
|
| 10 |
+
.env.production.local
|
| 11 |
+
|
| 12 |
+
# Build outputs
|
| 13 |
+
dist/
|
| 14 |
+
build/
|
| 15 |
+
*.log
|
| 16 |
+
|
| 17 |
+
# IDE
|
| 18 |
+
.vscode/
|
| 19 |
+
.idea/
|
| 20 |
+
*.swp
|
| 21 |
+
*.swo
|
| 22 |
+
*~
|
| 23 |
+
|
| 24 |
+
# OS
|
| 25 |
+
.DS_Store
|
| 26 |
+
Thumbs.db
|
| 27 |
+
|
| 28 |
+
# Uploads & Reports
|
| 29 |
+
backend/uploads/*
|
| 30 |
+
!backend/uploads/.gitkeep
|
| 31 |
+
backend/reports/*
|
| 32 |
+
!backend/reports/.gitkeep
|
| 33 |
+
|
| 34 |
+
# Testing
|
| 35 |
+
coverage/
|
| 36 |
+
.nyc_output/
|
| 37 |
+
|
| 38 |
+
# Misc
|
| 39 |
+
*.orig
|
| 40 |
+
.cache/
|
API_KEY_FIX.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🔴 GEMINI API KEY ISSUE - URGENT FIX NEEDED
|
| 2 |
+
|
| 3 |
+
## Problem Identified
|
| 4 |
+
**Your Gemini API key is NOT working!**
|
| 5 |
+
|
| 6 |
+
All model attempts failed:
|
| 7 |
+
- ❌ gemini-pro
|
| 8 |
+
- ❌ gemini-1.5-pro
|
| 9 |
+
- ❌ gemini-1.5-flash
|
| 10 |
+
- ❌ gemini-1.5-flash-latest
|
| 11 |
+
|
| 12 |
+
## Possible Reasons:
|
| 13 |
+
|
| 14 |
+
### 1. **API Key Invalid**
|
| 15 |
+
- Key might be expired
|
| 16 |
+
- Key might be for wrong project
|
| 17 |
+
- Typo in the key
|
| 18 |
+
|
| 19 |
+
### 2. **Quota Exceeded**
|
| 20 |
+
- Free tier limit reached
|
| 21 |
+
- Daily request limit exceeded
|
| 22 |
+
|
| 23 |
+
### 3. **API Key Not Activated**
|
| 24 |
+
- Gemini API not enabled in Google Cloud Console
|
| 25 |
+
|
| 26 |
+
## 🚀 SOLUTION - Get New API Key
|
| 27 |
+
|
| 28 |
+
### Step 1: Go to Google AI Studio
|
| 29 |
+
**URL:** https://aistudio.google.com/app/apikey
|
| 30 |
+
|
| 31 |
+
### Step 2: Create New API Key
|
| 32 |
+
1. Click "Create API Key"
|
| 33 |
+
2. Select "Create API key in new project" (or existing)
|
| 34 |
+
3. Copy the NEW key
|
| 35 |
+
|
| 36 |
+
### Step 3: Update .env File
|
| 37 |
+
Replace in `backend/.env`:
|
| 38 |
+
```env
|
| 39 |
+
GEMINI_API_KEY=your_new_api_key_here
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
### Step 4: Restart Backend
|
| 43 |
+
```bash
|
| 44 |
+
# Stop current server (Ctrl+C in backend terminal)
|
| 45 |
+
cd backend
|
| 46 |
+
npm run dev
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
## ⚡ Quick Test After New Key
|
| 50 |
+
```bash
|
| 51 |
+
cd backend
|
| 52 |
+
node test-gemini.js
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
You should see:
|
| 56 |
+
```
|
| 57 |
+
✅ SUCCESS: gemini-pro works!
|
| 58 |
+
Response: Hello
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
## Current API Key (First/Last 5 chars):
|
| 62 |
+
```
|
| 63 |
+
AIzaS...cGcFw
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
**This key is NOT working!** Get a new one from Google AI Studio.
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
## Alternative: Use OpenAI Instead
|
| 71 |
+
|
| 72 |
+
If Gemini keeps failing, we can switch to OpenAI:
|
| 73 |
+
|
| 74 |
+
1. Get OpenAI API key: https://platform.openai.com/api-keys
|
| 75 |
+
2. Install: `npm install openai`
|
| 76 |
+
3. I'll update the code to use GPT-4
|
| 77 |
+
|
| 78 |
+
**Let me know:**
|
| 79 |
+
- A) I'll get new Gemini key
|
| 80 |
+
- B) Switch to OpenAI instead
|
APPLICATION_RUNNING.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 RAG Document Analysis - Application Running!
|
| 2 |
+
|
| 3 |
+
## ✅ Current Status
|
| 4 |
+
|
| 5 |
+
**Application is LIVE and running!**
|
| 6 |
+
|
| 7 |
+
### Servers Status:
|
| 8 |
+
- ✅ **Backend**: Running on http://localhost:5000 (11+ minutes uptime)
|
| 9 |
+
- ✅ **Frontend**: Running on http://localhost:5173 (11+ minutes uptime)
|
| 10 |
+
- ✅ **Gemini API**: Configured and ready
|
| 11 |
+
- ✅ **File Storage**: Active (MongoDB optional)
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## 🎨 What You'll See in Browser
|
| 16 |
+
|
| 17 |
+
When you open **http://localhost:5173**, you'll see:
|
| 18 |
+
|
| 19 |
+
### **Header Section**
|
| 20 |
+
- 🎨 Animated gradient background (purple, pink, blue colors shifting)
|
| 21 |
+
- 📊 Gradient logo icon (rotates on hover)
|
| 22 |
+
- **"RAG Document Analysis"** title in gradient text
|
| 23 |
+
- 🇮🇳 "National Disaster Management Authority" subtitle
|
| 24 |
+
- Navigation buttons: Upload | Dashboard | Report
|
| 25 |
+
|
| 26 |
+
### **Upload Section** (Main Page)
|
| 27 |
+
- 💎 Beautiful glass panel with blur effect
|
| 28 |
+
- 📤 Large drag-and-drop upload area
|
| 29 |
+
- Purple gradient circle with upload icon
|
| 30 |
+
- Text: "Drag & drop your file here or click to browse"
|
| 31 |
+
- Supported formats: PDF, Excel, CSV (Max 10MB)
|
| 32 |
+
|
| 33 |
+
### **Enhanced UI Features**
|
| 34 |
+
- ✨ Smooth animations on all elements
|
| 35 |
+
- 🎯 Cards lift and scale on hover
|
| 36 |
+
- 💫 Buttons have shine animation effect
|
| 37 |
+
- 🌈 Background gradient shifts smoothly
|
| 38 |
+
- 💎 Premium glassmorphism throughout
|
| 39 |
+
|
| 40 |
+
---
|
| 41 |
+
|
| 42 |
+
## 🧪 How to Test RIGHT NOW
|
| 43 |
+
|
| 44 |
+
### **Step 1: Open Browser**
|
| 45 |
+
Your browser should already be open at:
|
| 46 |
+
```
|
| 47 |
+
http://localhost:5173
|
| 48 |
+
```
|
| 49 |
+
If not, manually type this in Chrome/Edge/Firefox
|
| 50 |
+
|
| 51 |
+
### **Step 2: Upload Test Data**
|
| 52 |
+
You have 3 CSV files ready:
|
| 53 |
+
|
| 54 |
+
1. **sample_training_data.csv** (50 records - original)
|
| 55 |
+
2. **test_data_1_recent.csv** (35 records - simple)
|
| 56 |
+
3. **test_data_2_detailed.csv** (45 records - detailed)
|
| 57 |
+
|
| 58 |
+
All files are in: `C:\Users\91979\OneDrive\Desktop\Rag Model\`
|
| 59 |
+
|
| 60 |
+
### **Step 3: Drag & Drop**
|
| 61 |
+
1. Open File Explorer (already opened)
|
| 62 |
+
2. Drag any CSV file to the upload area
|
| 63 |
+
3. Click **"Upload & Analyze"** button
|
| 64 |
+
|
| 65 |
+
### **Step 4: Wait for Analysis**
|
| 66 |
+
- Progress bar will show (0-100%)
|
| 67 |
+
- "Analyzing document..." message
|
| 68 |
+
- Takes 30-60 seconds (AI processing)
|
| 69 |
+
|
| 70 |
+
### **Step 5: View Results**
|
| 71 |
+
After analysis completes:
|
| 72 |
+
|
| 73 |
+
**Dashboard Tab:**
|
| 74 |
+
- 📊 Key metrics cards (trainings, participants, completion rate, states)
|
| 75 |
+
- 📈 Pie chart - Theme distribution
|
| 76 |
+
- 📊 Bar chart - State-wise coverage
|
| 77 |
+
- ⚠️ Gap analysis (underserved states/themes)
|
| 78 |
+
- 💡 Key insights
|
| 79 |
+
- ✅ Recommendations
|
| 80 |
+
|
| 81 |
+
**Report Tab:**
|
| 82 |
+
- 📄 Government-format report preview
|
| 83 |
+
- 📥 Download PDF button
|
| 84 |
+
- Professional NDMA format
|
| 85 |
+
|
| 86 |
+
---
|
| 87 |
+
|
| 88 |
+
## 🎯 What to Expect from Each Dataset
|
| 89 |
+
|
| 90 |
+
### **test_data_1_recent.csv** (Simple Test)
|
| 91 |
+
**Expected Results:**
|
| 92 |
+
- Total Trainings: 35
|
| 93 |
+
- Total Participants: ~2,800
|
| 94 |
+
- States Covered: 25+
|
| 95 |
+
- Themes: Earthquake, Flood, Cyclone, Fire, etc.
|
| 96 |
+
- Completion Rate: 82-99%
|
| 97 |
+
- Time Period: Oct-Dec 2024
|
| 98 |
+
|
| 99 |
+
### **test_data_2_detailed.csv** (Advanced Test)
|
| 100 |
+
**Expected Results:**
|
| 101 |
+
- Total Trainings: 45
|
| 102 |
+
- Total Participants: ~4,500
|
| 103 |
+
- States Covered: 28+
|
| 104 |
+
- Budget Analysis: ~₹17-18 million total
|
| 105 |
+
- Languages: Hindi, English, regional languages
|
| 106 |
+
- Certifications: Mix of certified/non-certified
|
| 107 |
+
- Time Period: Jan-Dec 2024
|
| 108 |
+
|
| 109 |
+
---
|
| 110 |
+
|
| 111 |
+
## 🎨 UI Highlights You'll See
|
| 112 |
+
|
| 113 |
+
### **Animated Background**
|
| 114 |
+
- Gradient shifts between purple, pink, and blue
|
| 115 |
+
- Smooth 15-second animation loop
|
| 116 |
+
- Creates premium, dynamic feel
|
| 117 |
+
|
| 118 |
+
### **Glass Panels**
|
| 119 |
+
- Frosted glass effect with blur
|
| 120 |
+
- Subtle shadows and borders
|
| 121 |
+
- Lift on hover with smooth transition
|
| 122 |
+
|
| 123 |
+
### **Buttons**
|
| 124 |
+
- Gradient colors (blue to purple)
|
| 125 |
+
- Shine animation on hover
|
| 126 |
+
- Scale and lift effects
|
| 127 |
+
|
| 128 |
+
### **Charts**
|
| 129 |
+
- Colorful pie chart for themes
|
| 130 |
+
- Blue bar chart for states
|
| 131 |
+
- Interactive tooltips
|
| 132 |
+
|
| 133 |
+
---
|
| 134 |
+
|
| 135 |
+
## 🔧 Troubleshooting
|
| 136 |
+
|
| 137 |
+
**If page doesn't load:**
|
| 138 |
+
1. Check terminals are still running
|
| 139 |
+
2. Refresh browser (Ctrl + R)
|
| 140 |
+
3. Clear cache (Ctrl + Shift + R)
|
| 141 |
+
|
| 142 |
+
**If upload fails:**
|
| 143 |
+
1. Check file size (< 10MB)
|
| 144 |
+
2. Verify file format (CSV/Excel/PDF)
|
| 145 |
+
3. Check backend terminal for errors
|
| 146 |
+
|
| 147 |
+
**If analysis takes too long:**
|
| 148 |
+
1. Wait up to 2 minutes
|
| 149 |
+
2. Check Gemini API key in backend/.env
|
| 150 |
+
3. Check backend terminal for API errors
|
| 151 |
+
|
| 152 |
+
---
|
| 153 |
+
|
| 154 |
+
## 📸 What You Should See
|
| 155 |
+
|
| 156 |
+
**Upload Page:**
|
| 157 |
+
```
|
| 158 |
+
┌─────────────────────────────────────────┐
|
| 159 |
+
│ 📊 RAG Document Analysis │
|
| 160 |
+
│ 🇮🇳 National Disaster Management │
|
| 161 |
+
│ [Upload] [Dashboard] [Report] │
|
| 162 |
+
├─────────────────────────────────────────┤
|
| 163 |
+
│ │
|
| 164 |
+
│ ┌───────────────────────────────────┐ │
|
| 165 |
+
│ │ Upload Training Data │ │
|
| 166 |
+
│ │ │ │
|
| 167 |
+
│ │ ┌─────────────────────────────┐ │ │
|
| 168 |
+
│ │ │ 📤 │ │ │
|
| 169 |
+
│ │ │ Drag & drop your file │ │ │
|
| 170 |
+
│ │ │ or click to browse │ │ │
|
| 171 |
+
│ │ │ │ │ │
|
| 172 |
+
│ │ │ PDF, Excel, CSV (Max 10MB) │ │ │
|
| 173 |
+
│ │ └─────────────────────────────┘ │ │
|
| 174 |
+
│ │ │ │
|
| 175 |
+
│ │ [Upload & Analyze] │ │
|
| 176 |
+
│ └───────────────────────────────────┘ │
|
| 177 |
+
│ │
|
| 178 |
+
└─────────────────────────────────────────┘
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
---
|
| 182 |
+
|
| 183 |
+
## ✅ Everything is Ready!
|
| 184 |
+
|
| 185 |
+
- ✅ Servers running
|
| 186 |
+
- ✅ UI enhanced with animations
|
| 187 |
+
- ✅ Test data files created
|
| 188 |
+
- ✅ Gemini API configured
|
| 189 |
+
- ✅ Browser opened
|
| 190 |
+
|
| 191 |
+
**Just drag a CSV file and test it!** 🚀
|
| 192 |
+
|
| 193 |
+
---
|
| 194 |
+
|
| 195 |
+
**Created:** 09-12-2024 02:59 AM
|
| 196 |
+
**Status:** Application Running Successfully
|
BROWSER_FIX.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Quick Fix Guide
|
| 2 |
+
|
| 3 |
+
## Problem: Browser Not Loading
|
| 4 |
+
|
| 5 |
+
### Step 1: Open Browser Manually
|
| 6 |
+
1. Open Chrome/Edge
|
| 7 |
+
2. Type in address bar: `localhost:5173`
|
| 8 |
+
3. Press Enter
|
| 9 |
+
|
| 10 |
+
### Step 2: If Still Not Working
|
| 11 |
+
1. Clear browser cache: Ctrl + Shift + Delete
|
| 12 |
+
2. Select "Cached images and files"
|
| 13 |
+
3. Click "Clear data"
|
| 14 |
+
4. Refresh: Ctrl + F5
|
| 15 |
+
|
| 16 |
+
### Step 3: Check Console
|
| 17 |
+
1. Press F12 in browser
|
| 18 |
+
2. Go to Console tab
|
| 19 |
+
3. Look for any red errors
|
| 20 |
+
4. Share screenshot if errors appear
|
| 21 |
+
|
| 22 |
+
### Direct URL
|
| 23 |
+
```
|
| 24 |
+
http://localhost:5173
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## Servers Status
|
| 28 |
+
✅ Backend: Running on port 5000
|
| 29 |
+
✅ Frontend: Running on port 5173
|
| 30 |
+
|
| 31 |
+
## If Nothing Works
|
| 32 |
+
Try this in PowerShell:
|
| 33 |
+
```powershell
|
| 34 |
+
cd "c:\Users\91979\OneDrive\Desktop\Rag Model\frontend"
|
| 35 |
+
npm run dev
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
Then open: http://localhost:5173
|
HOW_IT_WORKS.md
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🎯 RAG Document Analysis System - Complete Working Guide
|
| 2 |
+
|
| 3 |
+
## 📋 System Overview
|
| 4 |
+
|
| 5 |
+
Yeh ek **AI-powered Training Data Analysis System** hai jo disaster management training data ko analyze karta hai aur professional reports generate karta hai.
|
| 6 |
+
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
## 🏗️ Architecture (Kaise Kaam Karta Hai)
|
| 10 |
+
|
| 11 |
+
### **1. Frontend (React + Vite)**
|
| 12 |
+
**Location:** `frontend/` folder
|
| 13 |
+
|
| 14 |
+
**Main Components:**
|
| 15 |
+
- **App.jsx** - Main application, routing handle karta hai
|
| 16 |
+
- **FileUpload.jsx** - File upload interface (drag & drop)
|
| 17 |
+
- **AnalysisDashboard.jsx** - Analysis results dikhata hai
|
| 18 |
+
- **ReportPreview.jsx** - PDF report preview
|
| 19 |
+
|
| 20 |
+
**Flow:**
|
| 21 |
+
```
|
| 22 |
+
User uploads CSV/Excel → FileUpload component → Backend API → Analysis → Dashboard shows results
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
### **2. Backend (Node.js + Express)**
|
| 28 |
+
**Location:** `backend/` folder
|
| 29 |
+
|
| 30 |
+
**Main Files:**
|
| 31 |
+
|
| 32 |
+
#### **server.js**
|
| 33 |
+
- Express server start karta hai (Port 5000)
|
| 34 |
+
- MongoDB connection (optional)
|
| 35 |
+
- Routes setup karta hai
|
| 36 |
+
- CORS enable karta hai
|
| 37 |
+
|
| 38 |
+
#### **routes/upload.js**
|
| 39 |
+
Upload aur analysis ka main logic:
|
| 40 |
+
```javascript
|
| 41 |
+
1. File upload (Multer)
|
| 42 |
+
2. File parse (CSV/Excel → Text)
|
| 43 |
+
3. AI Analysis (Local rule-based)
|
| 44 |
+
4. PDF Report generation
|
| 45 |
+
5. Response send to frontend
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
#### **services/documentParser.js**
|
| 49 |
+
Files ko parse karta hai:
|
| 50 |
+
- **CSV files** → csv-parser use karke
|
| 51 |
+
- **Excel files** → xlsx library use karke
|
| 52 |
+
- **PDF files** → pdf-parse use karke
|
| 53 |
+
|
| 54 |
+
#### **services/analysisService.js**
|
| 55 |
+
**LOCAL AI ANALYSIS** (No external API needed!):
|
| 56 |
+
```javascript
|
| 57 |
+
analyzeTrainingData(rawData) {
|
| 58 |
+
// 1. Parse CSV data
|
| 59 |
+
// 2. Calculate metrics:
|
| 60 |
+
// - Total trainings
|
| 61 |
+
// - Total participants
|
| 62 |
+
// - State-wise coverage
|
| 63 |
+
// - Theme distribution
|
| 64 |
+
// - Completion rates
|
| 65 |
+
|
| 66 |
+
// 3. Generate insights:
|
| 67 |
+
// - Top performing states
|
| 68 |
+
// - Popular themes
|
| 69 |
+
// - Coverage gaps
|
| 70 |
+
|
| 71 |
+
// 4. Gap Analysis:
|
| 72 |
+
// - Underserved states
|
| 73 |
+
// - Missing themes
|
| 74 |
+
// - Critical gaps
|
| 75 |
+
|
| 76 |
+
// 5. Recommendations:
|
| 77 |
+
// - Based on data patterns
|
| 78 |
+
// - Actionable suggestions
|
| 79 |
+
}
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
**Important:** Yeh **100% FREE** hai - koi external AI API nahi use hota!
|
| 83 |
+
|
| 84 |
+
#### **services/reportGenerator.js**
|
| 85 |
+
Professional PDF report banata hai:
|
| 86 |
+
```javascript
|
| 87 |
+
generateReport() {
|
| 88 |
+
1. Cover Page (Government format)
|
| 89 |
+
2. Executive Summary
|
| 90 |
+
3. Key Metrics (5 cards)
|
| 91 |
+
4. Detailed Analysis (charts data)
|
| 92 |
+
5. Gap Analysis
|
| 93 |
+
6. Business Insights
|
| 94 |
+
7. Recommendations
|
| 95 |
+
8. Footer
|
| 96 |
+
}
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
Uses **PDFKit** library for PDF generation.
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
## 🔄 Complete Data Flow
|
| 104 |
+
|
| 105 |
+
### **Step 1: File Upload**
|
| 106 |
+
```
|
| 107 |
+
User → Drag CSV file → FileUpload component
|
| 108 |
+
→ Axios POST to /api/upload
|
| 109 |
+
→ Multer saves file to backend/uploads/
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
### **Step 2: File Parsing**
|
| 113 |
+
```
|
| 114 |
+
Backend → documentParser.js
|
| 115 |
+
→ Reads CSV/Excel
|
| 116 |
+
→ Converts to text format
|
| 117 |
+
→ Returns structured data
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
### **Step 3: AI Analysis (LOCAL)**
|
| 121 |
+
```
|
| 122 |
+
analysisService.js:
|
| 123 |
+
1. Parse rows from CSV
|
| 124 |
+
2. Count trainings, participants
|
| 125 |
+
3. Group by state, theme
|
| 126 |
+
4. Calculate percentages
|
| 127 |
+
5. Identify gaps (states with < 5 trainings)
|
| 128 |
+
6. Generate insights from patterns
|
| 129 |
+
7. Create recommendations
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
**Example Analysis Logic:**
|
| 133 |
+
```javascript
|
| 134 |
+
// Count trainings per state
|
| 135 |
+
const stateWiseCoverage = {};
|
| 136 |
+
rows.forEach(row => {
|
| 137 |
+
const state = row.State;
|
| 138 |
+
stateWiseCoverage[state] = (stateWiseCoverage[state] || 0) + 1;
|
| 139 |
+
});
|
| 140 |
+
|
| 141 |
+
// Find underserved states
|
| 142 |
+
const underservedStates = Object.entries(stateWiseCoverage)
|
| 143 |
+
.filter(([state, count]) => count < 5)
|
| 144 |
+
.map(([state]) => state);
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
### **Step 4: PDF Generation**
|
| 148 |
+
```
|
| 149 |
+
reportGenerator.js:
|
| 150 |
+
→ Creates PDFDocument
|
| 151 |
+
→ Adds sections with colors, boxes
|
| 152 |
+
→ Saves to backend/reports/
|
| 153 |
+
→ Returns file path
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
### **Step 5: Response to Frontend**
|
| 157 |
+
```json
|
| 158 |
+
{
|
| 159 |
+
"success": true,
|
| 160 |
+
"analysis": {
|
| 161 |
+
"totalTrainings": 45,
|
| 162 |
+
"totalParticipants": 1250,
|
| 163 |
+
"stateWiseCoverage": {...},
|
| 164 |
+
"themeDistribution": {...},
|
| 165 |
+
"keyInsights": [...],
|
| 166 |
+
"recommendations": [...],
|
| 167 |
+
"gapAnalysis": {...}
|
| 168 |
+
},
|
| 169 |
+
"executiveSummary": "...",
|
| 170 |
+
"reportUrl": "/reports/NDMA_Training_Report_123456.pdf"
|
| 171 |
+
}
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
### **Step 6: Dashboard Display**
|
| 175 |
+
```
|
| 176 |
+
AnalysisDashboard.jsx:
|
| 177 |
+
→ Shows 4 stat cards (top)
|
| 178 |
+
→ Shows charts (left side)
|
| 179 |
+
→ Shows business insights (right sidebar)
|
| 180 |
+
→ Shows recommendations (full width bottom)
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
---
|
| 184 |
+
|
| 185 |
+
## 📊 Dashboard Layout
|
| 186 |
+
|
| 187 |
+
```
|
| 188 |
+
┌─────────────────────────────────────────────────┐
|
| 189 |
+
│ [Trainings] [Participants] [Rate] [States] │ ← Stats (horizontal)
|
| 190 |
+
├──────────────────────────┬───��──────────────────┤
|
| 191 |
+
│ 📊 Theme Chart │ 💡 Business │
|
| 192 |
+
│ 📍 State Chart │ Insights │
|
| 193 |
+
│ ⚠️ Gap Analysis │ (Sidebar) │
|
| 194 |
+
├──────────────────────────┴──────────────────────┤
|
| 195 |
+
│ ✅ Recommendations (Full Width - 3 columns) │
|
| 196 |
+
└─────────────────────────────────────────────────┘
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
---
|
| 200 |
+
|
| 201 |
+
## 🎨 UI Features
|
| 202 |
+
|
| 203 |
+
### **Premium Design:**
|
| 204 |
+
- Whitish gradient background
|
| 205 |
+
- Glass morphism cards
|
| 206 |
+
- Hover animations
|
| 207 |
+
- Color-coded sections
|
| 208 |
+
- Professional typography (Inter font)
|
| 209 |
+
|
| 210 |
+
### **Interactive Elements:**
|
| 211 |
+
- Hover effects on cards
|
| 212 |
+
- Smooth transitions
|
| 213 |
+
- Responsive layout
|
| 214 |
+
- Clean spacing
|
| 215 |
+
|
| 216 |
+
---
|
| 217 |
+
|
| 218 |
+
## 📄 PDF Report Structure
|
| 219 |
+
|
| 220 |
+
```
|
| 221 |
+
1. Cover Page
|
| 222 |
+
- Government header
|
| 223 |
+
- Title in colored box
|
| 224 |
+
- Report details
|
| 225 |
+
- Confidentiality notice
|
| 226 |
+
|
| 227 |
+
2. Executive Summary
|
| 228 |
+
- Blue background box
|
| 229 |
+
- Summary text
|
| 230 |
+
|
| 231 |
+
3. Key Metrics (5 cards)
|
| 232 |
+
- 🎯 Total Trainings
|
| 233 |
+
- 👥 Participants
|
| 234 |
+
- 📈 Completion Rate
|
| 235 |
+
- 📍 States
|
| 236 |
+
- 🎓 Themes
|
| 237 |
+
|
| 238 |
+
4. Detailed Analysis
|
| 239 |
+
- Theme distribution list
|
| 240 |
+
- Top 10 states list
|
| 241 |
+
|
| 242 |
+
5. Gap Analysis
|
| 243 |
+
- Underserved states
|
| 244 |
+
- Critical gaps
|
| 245 |
+
|
| 246 |
+
6. Business Insights
|
| 247 |
+
- Numbered insights
|
| 248 |
+
|
| 249 |
+
7. Recommendations
|
| 250 |
+
- Strategic actions
|
| 251 |
+
|
| 252 |
+
8. Footer
|
| 253 |
+
- Copyright notice
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
---
|
| 257 |
+
|
| 258 |
+
## 🔑 Key Technologies
|
| 259 |
+
|
| 260 |
+
### **Frontend:**
|
| 261 |
+
- React 18
|
| 262 |
+
- Vite (build tool)
|
| 263 |
+
- Axios (API calls)
|
| 264 |
+
- Recharts (charts)
|
| 265 |
+
- React Dropzone (file upload)
|
| 266 |
+
- Lucide React (icons)
|
| 267 |
+
|
| 268 |
+
### **Backend:**
|
| 269 |
+
- Node.js
|
| 270 |
+
- Express.js
|
| 271 |
+
- Multer (file upload)
|
| 272 |
+
- csv-parser (CSV parsing)
|
| 273 |
+
- xlsx (Excel parsing)
|
| 274 |
+
- PDFKit (PDF generation)
|
| 275 |
+
- Mongoose (MongoDB - optional)
|
| 276 |
+
|
| 277 |
+
---
|
| 278 |
+
|
| 279 |
+
## 💾 Data Storage
|
| 280 |
+
|
| 281 |
+
### **Files:**
|
| 282 |
+
- **Uploads:** `backend/uploads/` (temporary)
|
| 283 |
+
- **Reports:** `backend/reports/` (generated PDFs)
|
| 284 |
+
|
| 285 |
+
### **MongoDB (Optional):**
|
| 286 |
+
```javascript
|
| 287 |
+
Document Schema:
|
| 288 |
+
{
|
| 289 |
+
originalName: String,
|
| 290 |
+
fileType: String,
|
| 291 |
+
uploadDate: Date,
|
| 292 |
+
extractedText: String,
|
| 293 |
+
analysis: Object,
|
| 294 |
+
executiveSummary: String,
|
| 295 |
+
reportUrl: String
|
| 296 |
+
}
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
**Note:** System works WITHOUT MongoDB too!
|
| 300 |
+
|
| 301 |
+
---
|
| 302 |
+
|
| 303 |
+
## 🚀 How to Use
|
| 304 |
+
|
| 305 |
+
### **1. Start Servers:**
|
| 306 |
+
```bash
|
| 307 |
+
# Terminal 1 - Backend
|
| 308 |
+
cd backend
|
| 309 |
+
npm run dev
|
| 310 |
+
|
| 311 |
+
# Terminal 2 - Frontend
|
| 312 |
+
cd frontend
|
| 313 |
+
npm run dev
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
### **2. Open Browser:**
|
| 317 |
+
```
|
| 318 |
+
http://localhost:5173
|
| 319 |
+
```
|
| 320 |
+
|
| 321 |
+
### **3. Upload File:**
|
| 322 |
+
- Click or drag CSV/Excel file
|
| 323 |
+
- Supported: `.csv`, `.xlsx`, `.xls`
|
| 324 |
+
- Max size: 10MB
|
| 325 |
+
|
| 326 |
+
### **4. View Results:**
|
| 327 |
+
- Dashboard shows automatically
|
| 328 |
+
- Charts, insights, recommendations
|
| 329 |
+
- Download PDF report
|
| 330 |
+
|
| 331 |
+
---
|
| 332 |
+
|
| 333 |
+
## 📁 File Structure
|
| 334 |
+
|
| 335 |
+
```
|
| 336 |
+
Rag Model/
|
| 337 |
+
├── backend/
|
| 338 |
+
│ ├── models/
|
| 339 |
+
│ │ └── Document.js # MongoDB schema
|
| 340 |
+
│ ├── routes/
|
| 341 |
+
│ │ ├── upload.js # Upload & analysis routes
|
| 342 |
+
│ │ └── analysis.js # Additional routes
|
| 343 |
+
│ ├── services/
|
| 344 |
+
│ │ ├── documentParser.js # File parsing
|
| 345 |
+
│ │ ├── analysisService.js # AI analysis (LOCAL)
|
| 346 |
+
│ │ └── reportGenerator.js # PDF generation
|
| 347 |
+
│ ├── uploads/ # Uploaded files
|
| 348 |
+
│ ├── reports/ # Generated PDFs
|
| 349 |
+
│ ├── server.js # Main server
|
| 350 |
+
│ ├── package.json
|
| 351 |
+
│ └── .env # Environment variables
|
| 352 |
+
│
|
| 353 |
+
├── frontend/
|
| 354 |
+
│ ├── src/
|
| 355 |
+
│ │ ├── components/
|
| 356 |
+
│ │ │ ├── FileUpload.jsx
|
| 357 |
+
│ │ │ ├── AnalysisDashboard.jsx
|
| 358 |
+
│ │ │ └── ReportPreview.jsx
|
| 359 |
+
│ │ ├── App.jsx
|
| 360 |
+
│ │ ├── main.jsx
|
| 361 |
+
│ │ └── index.css # Styles
|
| 362 |
+
│ ├── index.html
|
| 363 |
+
│ ├── package.json
|
| 364 |
+
│ └── vite.config.js
|
| 365 |
+
│
|
| 366 |
+
├── test_data_1_recent.csv # Sample data
|
| 367 |
+
├── test_data_2_detailed.csv # Sample data
|
| 368 |
+
└── README.md
|
| 369 |
+
```
|
| 370 |
+
|
| 371 |
+
---
|
| 372 |
+
|
| 373 |
+
## 🎯 Key Features
|
| 374 |
+
|
| 375 |
+
### **1. FREE AI Analysis**
|
| 376 |
+
- No API keys needed
|
| 377 |
+
- No quota limits
|
| 378 |
+
- 100% local processing
|
| 379 |
+
- Fast & reliable
|
| 380 |
+
|
| 381 |
+
### **2. Professional Dashboard**
|
| 382 |
+
- Real-time data visualization
|
| 383 |
+
- Interactive charts
|
| 384 |
+
- Color-coded insights
|
| 385 |
+
- Responsive design
|
| 386 |
+
|
| 387 |
+
### **3. Government-Format PDF**
|
| 388 |
+
- Official layout
|
| 389 |
+
- Colored sections
|
| 390 |
+
- Comprehensive data
|
| 391 |
+
- Professional formatting
|
| 392 |
+
|
| 393 |
+
### **4. Easy to Use**
|
| 394 |
+
- Drag & drop upload
|
| 395 |
+
- Automatic analysis
|
| 396 |
+
- One-click PDF download
|
| 397 |
+
- Clean interface
|
| 398 |
+
|
| 399 |
+
---
|
| 400 |
+
|
| 401 |
+
## 🔧 Environment Variables
|
| 402 |
+
|
| 403 |
+
**backend/.env:**
|
| 404 |
+
```env
|
| 405 |
+
PORT=5000
|
| 406 |
+
MONGODB_URI=mongodb://localhost:27017/rag-document-analysis
|
| 407 |
+
CLIENT_URL=http://localhost:5173
|
| 408 |
+
```
|
| 409 |
+
|
| 410 |
+
**Note:** MongoDB is optional!
|
| 411 |
+
|
| 412 |
+
---
|
| 413 |
+
|
| 414 |
+
## 📊 Sample Data Format
|
| 415 |
+
|
| 416 |
+
**CSV Structure:**
|
| 417 |
+
```csv
|
| 418 |
+
Training_ID,Theme,State,Participants,Duration,Completion_Rate,Date
|
| 419 |
+
T001,Earthquake,Delhi,45,3,95%,2024-10-15
|
| 420 |
+
T002,Flood,Maharashtra,60,2,88%,2024-10-20
|
| 421 |
+
...
|
| 422 |
+
```
|
| 423 |
+
|
| 424 |
+
**Required Columns:**
|
| 425 |
+
- Training_ID
|
| 426 |
+
- Theme (disaster type)
|
| 427 |
+
- State
|
| 428 |
+
- Participants (number)
|
| 429 |
+
- Duration (days)
|
| 430 |
+
- Completion_Rate (%)
|
| 431 |
+
- Date
|
| 432 |
+
|
| 433 |
+
---
|
| 434 |
+
|
| 435 |
+
## ✅ System Status
|
| 436 |
+
|
| 437 |
+
**Currently Running:**
|
| 438 |
+
- ✅ Backend: `http://localhost:5000`
|
| 439 |
+
- ✅ Frontend: `http://localhost:5173`
|
| 440 |
+
- ✅ File Upload: Working
|
| 441 |
+
- ✅ Analysis: Local (FREE)
|
| 442 |
+
- ✅ PDF Generation: Working
|
| 443 |
+
- ✅ Dashboard: Professional UI
|
| 444 |
+
|
| 445 |
+
**No Changes Needed - System is READY!** 🎉
|
| 446 |
+
|
| 447 |
+
---
|
| 448 |
+
|
| 449 |
+
## 🎓 How Analysis Works (Technical)
|
| 450 |
+
|
| 451 |
+
### **Data Extraction:**
|
| 452 |
+
```javascript
|
| 453 |
+
1. Read CSV rows
|
| 454 |
+
2. Parse each row into object
|
| 455 |
+
3. Extract fields: Theme, State, Participants, etc.
|
| 456 |
+
```
|
| 457 |
+
|
| 458 |
+
### **Metric Calculation:**
|
| 459 |
+
```javascript
|
| 460 |
+
totalTrainings = rows.length
|
| 461 |
+
totalParticipants = sum of all Participants
|
| 462 |
+
stateWiseCoverage = group by State, count
|
| 463 |
+
themeDistribution = group by Theme, count
|
| 464 |
+
averageCompletionRate = average of Completion_Rate
|
| 465 |
+
```
|
| 466 |
+
|
| 467 |
+
### **Insight Generation:**
|
| 468 |
+
```javascript
|
| 469 |
+
// Example: Top performing state
|
| 470 |
+
const topState = Object.entries(stateWiseCoverage)
|
| 471 |
+
.sort((a, b) => b[1] - a[1])[0];
|
| 472 |
+
|
| 473 |
+
insight = `${topState[0]} leads with ${topState[1]} trainings`;
|
| 474 |
+
```
|
| 475 |
+
|
| 476 |
+
### **Gap Identification:**
|
| 477 |
+
```javascript
|
| 478 |
+
// States with < 5 trainings
|
| 479 |
+
underservedStates = states.filter(count < 5);
|
| 480 |
+
|
| 481 |
+
// Themes with < 3 trainings
|
| 482 |
+
underservedThemes = themes.filter(count < 3);
|
| 483 |
+
```
|
| 484 |
+
|
| 485 |
+
### **Recommendations:**
|
| 486 |
+
```javascript
|
| 487 |
+
// Based on gaps
|
| 488 |
+
if (underservedStates.length > 0) {
|
| 489 |
+
recommendation = "Increase training coverage in: " + underservedStates.join(", ");
|
| 490 |
+
}
|
| 491 |
+
```
|
| 492 |
+
|
| 493 |
+
---
|
| 494 |
+
|
| 495 |
+
## 🎨 UI Color Scheme
|
| 496 |
+
|
| 497 |
+
- **Primary:** #3b82f6 (Blue)
|
| 498 |
+
- **Secondary:** #10b981 (Green)
|
| 499 |
+
- **Accent:** #f59e0b (Orange)
|
| 500 |
+
- **Warning:** #dc2626 (Red)
|
| 501 |
+
- **Background:** #f8fafc (Light)
|
| 502 |
+
- **Text:** #1f2937 (Dark Gray)
|
| 503 |
+
|
| 504 |
+
---
|
| 505 |
+
|
| 506 |
+
## 📱 Browser Compatibility
|
| 507 |
+
|
| 508 |
+
- ✅ Chrome
|
| 509 |
+
- ✅ Edge
|
| 510 |
+
- ✅ Firefox
|
| 511 |
+
- ✅ Safari
|
| 512 |
+
|
| 513 |
+
---
|
| 514 |
+
|
| 515 |
+
## 🔐 Security
|
| 516 |
+
|
| 517 |
+
- File type validation
|
| 518 |
+
- File size limits (10MB)
|
| 519 |
+
- CORS enabled for localhost
|
| 520 |
+
- No sensitive data stored (optional MongoDB)
|
| 521 |
+
|
| 522 |
+
---
|
| 523 |
+
|
| 524 |
+
## 🎉 Summary
|
| 525 |
+
|
| 526 |
+
**Yeh system:**
|
| 527 |
+
1. CSV/Excel files upload karta hai
|
| 528 |
+
2. Data ko parse karta hai
|
| 529 |
+
3. LOCAL AI se analysis karta hai (FREE!)
|
| 530 |
+
4. Professional dashboard dikhata hai
|
| 531 |
+
5. Government-format PDF report banata hai
|
| 532 |
+
|
| 533 |
+
**Sab kuch READY hai - koi change nahi chahiye!** ✅
|
| 534 |
+
|
| 535 |
+
**Test karne ke liye:**
|
| 536 |
+
1. Browser mein `http://localhost:5173` kholo
|
| 537 |
+
2. `test_data_1_recent.csv` upload karo
|
| 538 |
+
3. Dashboard dekho
|
| 539 |
+
4. PDF download karo
|
| 540 |
+
|
| 541 |
+
**System 100% working hai!** 🚀
|
HUGGINGFACE_SETUP.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🆓 FREE AI API - Hugging Face Setup
|
| 2 |
+
|
| 3 |
+
## Step 1: Get FREE Hugging Face API Token
|
| 4 |
+
|
| 5 |
+
1. **Visit:** https://huggingface.co/join
|
| 6 |
+
2. **Sign up** (completely FREE - no credit card needed!)
|
| 7 |
+
3. **Go to Settings:** https://huggingface.co/settings/tokens
|
| 8 |
+
4. Click **"New token"**
|
| 9 |
+
5. Name: `rag-analysis`
|
| 10 |
+
6. Type: **Read**
|
| 11 |
+
7. Click **"Generate"**
|
| 12 |
+
8. **Copy the token** (starts with `hf_...`)
|
| 13 |
+
|
| 14 |
+
## Step 2: Give Me the Token
|
| 15 |
+
|
| 16 |
+
Paste the Hugging Face token here, and I'll update the code!
|
| 17 |
+
|
| 18 |
+
Format: `hf_xxxxxxxxxxxxxxxxxxxxxxxxxx`
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
## Why Hugging Face?
|
| 23 |
+
|
| 24 |
+
✅ **Completely FREE** - No credit card required
|
| 25 |
+
✅ **No quota limits** for basic usage
|
| 26 |
+
✅ **Reliable** - Works consistently
|
| 27 |
+
✅ **Fast** - Good response times
|
| 28 |
+
✅ **Easy** - Just sign up and get token
|
| 29 |
+
|
| 30 |
+
## Models We'll Use
|
| 31 |
+
|
| 32 |
+
- **Mistral-7B** - For analysis (free, fast)
|
| 33 |
+
- **Llama-2** - Backup option (also free)
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
**Time needed:** 2 minutes to sign up + get token! 🎉
|
MONGODB_SETUP.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MongoDB Atlas Setup - Step by Step Guide
|
| 2 |
+
|
| 3 |
+
## 📋 Follow These Steps Exactly:
|
| 4 |
+
|
| 5 |
+
### Step 1: Create Account (2 minutes)
|
| 6 |
+
1. Open browser and go to: **https://www.mongodb.com/cloud/atlas/register**
|
| 7 |
+
2. Sign up with:
|
| 8 |
+
- **Email** (recommended) OR
|
| 9 |
+
- **Google account** (fastest)
|
| 10 |
+
3. Click "Create your Atlas account"
|
| 11 |
+
|
| 12 |
+
### Step 2: Create Free Cluster (3 minutes)
|
| 13 |
+
1. After login, you'll see "Create a deployment"
|
| 14 |
+
2. Choose **M0 FREE** tier (it's completely free!)
|
| 15 |
+
3. **Cloud Provider**: Select any (AWS recommended)
|
| 16 |
+
4. **Region**: Select closest to India (e.g., Mumbai, Singapore)
|
| 17 |
+
5. **Cluster Name**: Keep default or name it `rag-cluster`
|
| 18 |
+
6. Click **"Create Deployment"** button
|
| 19 |
+
7. Wait 1-3 minutes for cluster to be created
|
| 20 |
+
|
| 21 |
+
### Step 3: Create Database User (1 minute)
|
| 22 |
+
1. A popup will appear: "Security Quickstart"
|
| 23 |
+
2. **Username**: Enter `ragadmin` (or any name you want)
|
| 24 |
+
3. **Password**: Click "Autogenerate Secure Password"
|
| 25 |
+
- **IMPORTANT**: Copy this password and save it somewhere!
|
| 26 |
+
4. Click **"Create Database User"**
|
| 27 |
+
|
| 28 |
+
### Step 4: Add IP Address (1 minute)
|
| 29 |
+
1. Next screen: "Where would you like to connect from?"
|
| 30 |
+
2. Click **"Add My Current IP Address"** button
|
| 31 |
+
3. OR for testing, click **"Allow Access from Anywhere"** (0.0.0.0/0)
|
| 32 |
+
4. Click **"Finish and Close"**
|
| 33 |
+
|
| 34 |
+
### Step 5: Get Connection String (1 minute)
|
| 35 |
+
1. Click **"Go to Database"** or **"Connect"** button
|
| 36 |
+
2. Choose **"Drivers"** option
|
| 37 |
+
3. Select:
|
| 38 |
+
- Driver: **Node.js**
|
| 39 |
+
- Version: **5.5 or later**
|
| 40 |
+
4. Copy the connection string (looks like this):
|
| 41 |
+
```
|
| 42 |
+
mongodb+srv://ragadmin:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority
|
| 43 |
+
```
|
| 44 |
+
5. **IMPORTANT**: Replace `<password>` with the actual password you saved earlier!
|
| 45 |
+
|
| 46 |
+
### Step 6: Give Me the Connection String
|
| 47 |
+
**Paste the complete connection string here** (with password replaced), and I'll add it to your `.env` file!
|
| 48 |
+
|
| 49 |
+
Example format:
|
| 50 |
+
```
|
| 51 |
+
mongodb+srv://ragadmin:YourActualPassword123@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## 🎯 Quick Reference
|
| 57 |
+
|
| 58 |
+
**What you need to give me:**
|
| 59 |
+
- ✅ MongoDB Atlas connection string (with password)
|
| 60 |
+
|
| 61 |
+
**What I'll do:**
|
| 62 |
+
- ✅ Add it to `.env` file
|
| 63 |
+
- ✅ Test the connection
|
| 64 |
+
- ✅ Start the backend server
|
| 65 |
+
- ✅ Start the frontend
|
| 66 |
+
- ✅ Open the app in browser
|
| 67 |
+
|
| 68 |
+
---
|
| 69 |
+
|
| 70 |
+
## ⚠️ Common Issues
|
| 71 |
+
|
| 72 |
+
**Issue**: "Password contains special characters"
|
| 73 |
+
**Solution**: URL encode the password or regenerate a simpler one
|
| 74 |
+
|
| 75 |
+
**Issue**: "IP not whitelisted"
|
| 76 |
+
**Solution**: Add 0.0.0.0/0 in Network Access settings
|
| 77 |
+
|
| 78 |
+
**Issue**: "Connection timeout"
|
| 79 |
+
**Solution**: Check if cluster is active (green dot)
|
| 80 |
+
|
| 81 |
+
---
|
| 82 |
+
|
| 83 |
+
**Ready? Start with Step 1 and let me know when you have the connection string!** 🚀
|
README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# RAG Document Analysis System
|
| 2 |
+
|
| 3 |
+

|
| 4 |
+

|
| 5 |
+

|
| 6 |
+
|
| 7 |
+
A comprehensive RAG (Retrieval-Augmented Generation) based document analysis system for analyzing disaster management training data. Built for the National Disaster Management Authority (NDMA) as part of Smart India Hackathon 2025.
|
| 8 |
+
|
| 9 |
+
## 🌟 Features
|
| 10 |
+
|
| 11 |
+
- **📤 Multi-Format Upload**: Support for PDF, Excel (.xlsx, .xls), and CSV files
|
| 12 |
+
- **🤖 AI-Powered Analysis**: Uses Google Gemini AI for intelligent data analysis
|
| 13 |
+
- **📊 Interactive Dashboard**: Beautiful visualizations with charts and metrics
|
| 14 |
+
- **📄 Government Reports**: Auto-generate professional PDF reports in government format
|
| 15 |
+
- **🔍 RAG-based Q&A**: Ask questions about your uploaded documents
|
| 16 |
+
- **💾 Data Persistence**: MongoDB storage for analysis history
|
| 17 |
+
- **🎨 Modern UI**: Glassmorphism design with smooth animations
|
| 18 |
+
|
| 19 |
+
## 🏗️ Architecture
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
┌─────────────────┐
|
| 23 |
+
│ React Frontend │
|
| 24 |
+
│ (Vite + UI) │
|
| 25 |
+
└────────┬────────┘
|
| 26 |
+
│
|
| 27 |
+
▼
|
| 28 |
+
┌─────────────────┐
|
| 29 |
+
│ Express API │
|
| 30 |
+
│ (Node.js) │
|
| 31 |
+
└────────┬────────┘
|
| 32 |
+
│
|
| 33 |
+
┌────┴────┐
|
| 34 |
+
▼ ▼
|
| 35 |
+
┌────────┐ ┌──────────┐
|
| 36 |
+
│MongoDB │ │Gemini AI │
|
| 37 |
+
└────────┘ └──────────┘
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## 🚀 Quick Start
|
| 41 |
+
|
| 42 |
+
### Prerequisites
|
| 43 |
+
|
| 44 |
+
- Node.js (v18 or higher)
|
| 45 |
+
- MongoDB (local or Atlas)
|
| 46 |
+
- Google Gemini API Key
|
| 47 |
+
|
| 48 |
+
### Installation
|
| 49 |
+
|
| 50 |
+
1. **Clone the repository**
|
| 51 |
+
```bash
|
| 52 |
+
cd "c:\Users\91979\OneDrive\Desktop\Rag Model"
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
2. **Setup Backend**
|
| 56 |
+
```bash
|
| 57 |
+
cd backend
|
| 58 |
+
npm install
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
3. **Configure Environment Variables**
|
| 62 |
+
|
| 63 |
+
Edit `backend/.env` and add your credentials:
|
| 64 |
+
```env
|
| 65 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 66 |
+
MONGODB_URI=mongodb://localhost:27017/rag-document-analysis
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
4. **Setup Frontend**
|
| 70 |
+
```bash
|
| 71 |
+
cd ../frontend
|
| 72 |
+
npm install
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
### Running the Application
|
| 76 |
+
|
| 77 |
+
1. **Start MongoDB** (if running locally)
|
| 78 |
+
```bash
|
| 79 |
+
mongod
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
2. **Start Backend Server**
|
| 83 |
+
```bash
|
| 84 |
+
cd backend
|
| 85 |
+
npm run dev
|
| 86 |
+
```
|
| 87 |
+
Backend will run on `http://localhost:5000`
|
| 88 |
+
|
| 89 |
+
3. **Start Frontend** (in a new terminal)
|
| 90 |
+
```bash
|
| 91 |
+
cd frontend
|
| 92 |
+
npm run dev
|
| 93 |
+
```
|
| 94 |
+
Frontend will run on `http://localhost:5173`
|
| 95 |
+
|
| 96 |
+
4. **Open in Browser**
|
| 97 |
+
Navigate to `http://localhost:5173`
|
| 98 |
+
|
| 99 |
+
## 📖 Usage Guide
|
| 100 |
+
|
| 101 |
+
### 1. Upload Training Data
|
| 102 |
+
|
| 103 |
+
- Click on the upload area or drag & drop your file
|
| 104 |
+
- Supported formats: PDF, Excel, CSV
|
| 105 |
+
- Maximum file size: 10MB
|
| 106 |
+
- Click "Upload & Analyze"
|
| 107 |
+
|
| 108 |
+
### 2. View Analysis Dashboard
|
| 109 |
+
|
| 110 |
+
- Automatic analysis using AI
|
| 111 |
+
- View key metrics (trainings, participants, completion rates)
|
| 112 |
+
- Interactive charts for theme and state distribution
|
| 113 |
+
- Gap analysis showing underserved areas
|
| 114 |
+
- Key insights and recommendations
|
| 115 |
+
|
| 116 |
+
### 3. Download Report
|
| 117 |
+
|
| 118 |
+
- Click on "Report" tab
|
| 119 |
+
- Preview the government-format report
|
| 120 |
+
- Download as PDF
|
| 121 |
+
- Share with stakeholders
|
| 122 |
+
|
| 123 |
+
## 📁 Project Structure
|
| 124 |
+
|
| 125 |
+
```
|
| 126 |
+
Rag Model/
|
| 127 |
+
├── backend/
|
| 128 |
+
│ ├── models/
|
| 129 |
+
│ │ └── Document.js # MongoDB schema
|
| 130 |
+
│ ├── routes/
|
| 131 |
+
│ │ ├── upload.js # Upload endpoints
|
| 132 |
+
│ │ └── analysis.js # Analysis endpoints
|
| 133 |
+
│ ├── services/
|
| 134 |
+
│ │ ├── documentParser.js # PDF/Excel/CSV parser
|
| 135 |
+
│ │ ├── ragEngine.js # RAG implementation
|
| 136 |
+
│ │ ├── analysisService.js # AI analysis logic
|
| 137 |
+
│ │ └── reportGenerator.js # PDF report generation
|
| 138 |
+
│ ├── server.js # Express server
|
| 139 |
+
│ └── package.json
|
| 140 |
+
│
|
| 141 |
+
├── frontend/
|
| 142 |
+
│ ├── src/
|
| 143 |
+
│ │ ├── components/
|
| 144 |
+
│ │ │ ├── FileUpload.jsx # Upload component
|
| 145 |
+
│ │ │ ├── AnalysisDashboard.jsx # Dashboard
|
| 146 |
+
│ │ │ └── ReportPreview.jsx # Report preview
|
| 147 |
+
│ │ ├── App.jsx # Main app
|
| 148 |
+
│ │ ├── main.jsx # Entry point
|
| 149 |
+
│ │ └── index.css # Styles
|
| 150 |
+
│ ├── index.html
|
| 151 |
+
│ ├── vite.config.js
|
| 152 |
+
│ └── package.json
|
| 153 |
+
│
|
| 154 |
+
└── README.md
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
## 🔧 API Endpoints
|
| 158 |
+
|
| 159 |
+
### Upload & Analysis
|
| 160 |
+
- `POST /api/upload` - Upload and analyze document
|
| 161 |
+
- `GET /api/upload/documents` - Get all documents
|
| 162 |
+
- `GET /api/upload/document/:id` - Get specific document
|
| 163 |
+
- `DELETE /api/upload/document/:id` - Delete document
|
| 164 |
+
|
| 165 |
+
### Analysis & Reports
|
| 166 |
+
- `POST /api/analysis/ask` - Ask question about document (RAG)
|
| 167 |
+
- `GET /api/analysis/report/:filename` - Download report
|
| 168 |
+
- `GET /api/analysis/stats` - Get aggregate statistics
|
| 169 |
+
|
| 170 |
+
## 🎨 Tech Stack
|
| 171 |
+
|
| 172 |
+
**Backend:**
|
| 173 |
+
- Node.js + Express
|
| 174 |
+
- MongoDB + Mongoose
|
| 175 |
+
- Google Gemini AI
|
| 176 |
+
- PDFKit (report generation)
|
| 177 |
+
- Multer (file uploads)
|
| 178 |
+
- pdf-parse, xlsx, csv-parser
|
| 179 |
+
|
| 180 |
+
**Frontend:**
|
| 181 |
+
- React 18
|
| 182 |
+
- Vite
|
| 183 |
+
- Recharts (data visualization)
|
| 184 |
+
- React Dropzone
|
| 185 |
+
- Lucide React (icons)
|
| 186 |
+
- Axios
|
| 187 |
+
|
| 188 |
+
## 📊 Sample Data Format
|
| 189 |
+
|
| 190 |
+
### Excel/CSV Format
|
| 191 |
+
```csv
|
| 192 |
+
Training ID,Date,Location,State,Theme,Participants,Trainer,Duration,Completion Rate
|
| 193 |
+
TR001,2024-01-15,Delhi,Delhi,Earthquake,50,Dr. Sharma,2 days,95%
|
| 194 |
+
TR002,2024-01-20,Mumbai,Maharashtra,Flood,75,Mr. Patel,3 days,88%
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
### Analysis Output
|
| 198 |
+
```json
|
| 199 |
+
{
|
| 200 |
+
"totalTrainings": 150,
|
| 201 |
+
"totalParticipants": 5000,
|
| 202 |
+
"themeDistribution": {
|
| 203 |
+
"Earthquake": 40,
|
| 204 |
+
"Flood": 60,
|
| 205 |
+
"Cyclone": 30
|
| 206 |
+
},
|
| 207 |
+
"stateWiseCoverage": {
|
| 208 |
+
"Delhi": 20,
|
| 209 |
+
"Maharashtra": 35
|
| 210 |
+
},
|
| 211 |
+
"averageCompletionRate": "91%",
|
| 212 |
+
"gapAnalysis": {
|
| 213 |
+
"underservedStates": ["Nagaland", "Mizoram"],
|
| 214 |
+
"underservedThemes": ["Tsunami"]
|
| 215 |
+
},
|
| 216 |
+
"recommendations": [...]
|
| 217 |
+
}
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
## 🔐 Environment Variables
|
| 221 |
+
|
| 222 |
+
| Variable | Description | Required |
|
| 223 |
+
|----------|-------------|----------|
|
| 224 |
+
| `GEMINI_API_KEY` | Google Gemini API key | Yes |
|
| 225 |
+
| `MONGODB_URI` | MongoDB connection string | Yes |
|
| 226 |
+
| `PORT` | Backend server port | No (default: 5000) |
|
| 227 |
+
| `CLIENT_URL` | Frontend URL for CORS | No (default: http://localhost:5173) |
|
| 228 |
+
|
| 229 |
+
## 🤝 Contributing
|
| 230 |
+
|
| 231 |
+
This project was developed for Smart India Hackathon 2025 by Team JARVIS GGV.
|
| 232 |
+
|
| 233 |
+
## 📝 License
|
| 234 |
+
|
| 235 |
+
MIT License - Feel free to use for educational and government purposes.
|
| 236 |
+
|
| 237 |
+
## 👥 Team JARVIS GGV
|
| 238 |
+
|
| 239 |
+
Smart India Hackathon 2025
|
| 240 |
+
Problem Statement ID: SIH25258
|
| 241 |
+
Theme: Disaster Management
|
| 242 |
+
|
| 243 |
+
---
|
| 244 |
+
|
| 245 |
+
**For Official Use by NDMA, Government of India**
|
SETUP_GUIDE.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Quick Setup Guide
|
| 2 |
+
|
| 3 |
+
## 🚀 Getting Started in 5 Minutes
|
| 4 |
+
|
| 5 |
+
### Step 1: Install Dependencies
|
| 6 |
+
|
| 7 |
+
**Backend:**
|
| 8 |
+
```bash
|
| 9 |
+
cd backend
|
| 10 |
+
npm install
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
**Frontend:**
|
| 14 |
+
```bash
|
| 15 |
+
cd frontend
|
| 16 |
+
npm install
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
### Step 2: Get Gemini API Key
|
| 20 |
+
|
| 21 |
+
1. Go to [Google AI Studio](https://makersuite.google.com/app/apikey)
|
| 22 |
+
2. Create a new API key
|
| 23 |
+
3. Copy the API key
|
| 24 |
+
|
| 25 |
+
### Step 3: Configure Environment
|
| 26 |
+
|
| 27 |
+
Edit `backend/.env`:
|
| 28 |
+
```env
|
| 29 |
+
GEMINI_API_KEY=paste_your_api_key_here
|
| 30 |
+
MONGODB_URI=mongodb://localhost:27017/rag-document-analysis
|
| 31 |
+
PORT=5000
|
| 32 |
+
CLIENT_URL=http://localhost:5173
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
### Step 4: Start MongoDB
|
| 36 |
+
|
| 37 |
+
**Option A: Local MongoDB**
|
| 38 |
+
```bash
|
| 39 |
+
mongod
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
**Option B: MongoDB Atlas (Cloud)**
|
| 43 |
+
- Create free cluster at [MongoDB Atlas](https://www.mongodb.com/cloud/atlas)
|
| 44 |
+
- Get connection string
|
| 45 |
+
- Update `MONGODB_URI` in `.env`
|
| 46 |
+
|
| 47 |
+
### Step 5: Run the Application
|
| 48 |
+
|
| 49 |
+
**Terminal 1 - Backend:**
|
| 50 |
+
```bash
|
| 51 |
+
cd backend
|
| 52 |
+
npm run dev
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
**Terminal 2 - Frontend:**
|
| 56 |
+
```bash
|
| 57 |
+
cd frontend
|
| 58 |
+
npm run dev
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
### Step 6: Test with Sample Data
|
| 62 |
+
|
| 63 |
+
1. Open browser: `http://localhost:5173`
|
| 64 |
+
2. Upload the `sample_training_data.csv` file
|
| 65 |
+
3. Wait for analysis (30-60 seconds)
|
| 66 |
+
4. View dashboard and download report
|
| 67 |
+
|
| 68 |
+
## 🎯 Testing Checklist
|
| 69 |
+
|
| 70 |
+
- [ ] Backend server running on port 5000
|
| 71 |
+
- [ ] Frontend running on port 5173
|
| 72 |
+
- [ ] MongoDB connected successfully
|
| 73 |
+
- [ ] Upload sample CSV file
|
| 74 |
+
- [ ] View analysis dashboard
|
| 75 |
+
- [ ] Download PDF report
|
| 76 |
+
- [ ] Check report format
|
| 77 |
+
|
| 78 |
+
## ⚠️ Common Issues
|
| 79 |
+
|
| 80 |
+
### Issue: "MongoDB connection failed"
|
| 81 |
+
**Solution:** Make sure MongoDB is running or use MongoDB Atlas
|
| 82 |
+
|
| 83 |
+
### Issue: "Gemini API error"
|
| 84 |
+
**Solution:** Check if API key is valid and has quota
|
| 85 |
+
|
| 86 |
+
### Issue: "Cannot upload file"
|
| 87 |
+
**Solution:** Check file size (max 10MB) and format (PDF/Excel/CSV)
|
| 88 |
+
|
| 89 |
+
### Issue: "Port already in use"
|
| 90 |
+
**Solution:** Change port in `.env` or kill existing process
|
| 91 |
+
|
| 92 |
+
## 📞 Need Help?
|
| 93 |
+
|
| 94 |
+
Check the main [README.md](README.md) for detailed documentation.
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
**Team JARVIS GGV | SIH 2025**
|
backend/.env.example
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Server Configuration
|
| 2 |
+
PORT=5000
|
| 3 |
+
NODE_ENV=development
|
| 4 |
+
|
| 5 |
+
# MongoDB Configuration
|
| 6 |
+
MONGODB_URI=mongodb://localhost:27017/rag-document-analysis
|
| 7 |
+
|
| 8 |
+
# Google Gemini AI Configuration
|
| 9 |
+
GEMINI_API_KEY=your_gemini_api_key_here
|
| 10 |
+
|
| 11 |
+
# Cloudinary Configuration (Optional - for file storage)
|
| 12 |
+
CLOUDINARY_CLOUD_NAME=your_cloud_name
|
| 13 |
+
CLOUDINARY_API_KEY=your_api_key
|
| 14 |
+
CLOUDINARY_API_SECRET=your_api_secret
|
| 15 |
+
|
| 16 |
+
# CORS Configuration
|
| 17 |
+
CLIENT_URL=http://localhost:5173
|
backend/.gitignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
.env
|
| 3 |
+
uploads/
|
| 4 |
+
*.log
|
| 5 |
+
.DS_Store
|
| 6 |
+
dist/
|
| 7 |
+
build/
|
backend/models/Document.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import mongoose from 'mongoose';
|
| 2 |
+
|
| 3 |
+
const documentSchema = new mongoose.Schema({
|
| 4 |
+
filename: {
|
| 5 |
+
type: String,
|
| 6 |
+
required: true
|
| 7 |
+
},
|
| 8 |
+
originalName: {
|
| 9 |
+
type: String,
|
| 10 |
+
required: true
|
| 11 |
+
},
|
| 12 |
+
fileType: {
|
| 13 |
+
type: String,
|
| 14 |
+
enum: ['pdf', 'excel', 'csv'],
|
| 15 |
+
required: true
|
| 16 |
+
},
|
| 17 |
+
fileSize: {
|
| 18 |
+
type: Number,
|
| 19 |
+
required: true
|
| 20 |
+
},
|
| 21 |
+
uploadDate: {
|
| 22 |
+
type: Date,
|
| 23 |
+
default: Date.now
|
| 24 |
+
},
|
| 25 |
+
status: {
|
| 26 |
+
type: String,
|
| 27 |
+
enum: ['uploaded', 'processing', 'analyzed', 'error'],
|
| 28 |
+
default: 'uploaded'
|
| 29 |
+
},
|
| 30 |
+
extractedText: {
|
| 31 |
+
type: String
|
| 32 |
+
},
|
| 33 |
+
analysisResults: {
|
| 34 |
+
totalTrainings: Number,
|
| 35 |
+
totalParticipants: Number,
|
| 36 |
+
themeDistribution: mongoose.Schema.Types.Mixed,
|
| 37 |
+
stateWiseCoverage: mongoose.Schema.Types.Mixed,
|
| 38 |
+
averageCompletionRate: String,
|
| 39 |
+
gapAnalysis: mongoose.Schema.Types.Mixed,
|
| 40 |
+
recommendations: [String],
|
| 41 |
+
keyInsights: [String]
|
| 42 |
+
},
|
| 43 |
+
reportUrl: {
|
| 44 |
+
type: String
|
| 45 |
+
},
|
| 46 |
+
cloudinaryUrl: {
|
| 47 |
+
type: String
|
| 48 |
+
}
|
| 49 |
+
}, {
|
| 50 |
+
timestamps: true
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
export default mongoose.model('Document', documentSchema);
|
backend/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "rag-document-analysis-backend",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "Backend for RAG-based document analysis system for disaster management training data",
|
| 5 |
+
"main": "server.js",
|
| 6 |
+
"type": "module",
|
| 7 |
+
"scripts": {
|
| 8 |
+
"start": "node server.js",
|
| 9 |
+
"dev": "nodemon server.js"
|
| 10 |
+
},
|
| 11 |
+
"keywords": [
|
| 12 |
+
"rag",
|
| 13 |
+
"document-analysis",
|
| 14 |
+
"disaster-management",
|
| 15 |
+
"ai"
|
| 16 |
+
],
|
| 17 |
+
"author": "JARVIS GGV",
|
| 18 |
+
"license": "MIT",
|
| 19 |
+
"dependencies": {
|
| 20 |
+
"@google/generative-ai": "^0.24.1",
|
| 21 |
+
"@huggingface/inference": "^4.13.5",
|
| 22 |
+
"cloudinary": "^1.41.3",
|
| 23 |
+
"cors": "^2.8.5",
|
| 24 |
+
"csv-parser": "^3.0.0",
|
| 25 |
+
"dotenv": "^16.3.1",
|
| 26 |
+
"express": "^4.18.2",
|
| 27 |
+
"mongoose": "^8.0.3",
|
| 28 |
+
"multer": "^1.4.5-lts.1",
|
| 29 |
+
"node-fetch": "^3.3.2",
|
| 30 |
+
"openai": "^6.10.0",
|
| 31 |
+
"pdf-parse": "^1.1.1",
|
| 32 |
+
"pdfkit": "^0.14.0",
|
| 33 |
+
"xlsx": "^0.18.5"
|
| 34 |
+
},
|
| 35 |
+
"devDependencies": {
|
| 36 |
+
"nodemon": "^3.0.2"
|
| 37 |
+
}
|
| 38 |
+
}
|
backend/routes/analysis.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import express from 'express';
|
| 2 |
+
import Document from '../models/Document.js';
|
| 3 |
+
import analysisService from '../services/analysisService.js';
|
| 4 |
+
import path from 'path';
|
| 5 |
+
import fs from 'fs';
|
| 6 |
+
import { fileURLToPath } from 'url';
|
| 7 |
+
|
| 8 |
+
const __filename = fileURLToPath(import.meta.url);
|
| 9 |
+
const __dirname = path.dirname(__filename);
|
| 10 |
+
|
| 11 |
+
const router = express.Router();
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* POST /api/analysis/ask
|
| 15 |
+
* Ask a question about uploaded document using RAG
|
| 16 |
+
*/
|
| 17 |
+
router.post('/ask', async (req, res) => {
|
| 18 |
+
try {
|
| 19 |
+
const { documentId, question } = req.body;
|
| 20 |
+
|
| 21 |
+
if (!documentId || !question) {
|
| 22 |
+
return res.status(400).json({
|
| 23 |
+
error: 'Document ID and question are required'
|
| 24 |
+
});
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
const document = await Document.findById(documentId);
|
| 28 |
+
|
| 29 |
+
if (!document) {
|
| 30 |
+
return res.status(404).json({ error: 'Document not found' });
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
if (!document.extractedText) {
|
| 34 |
+
return res.status(400).json({
|
| 35 |
+
error: 'Document has not been processed yet'
|
| 36 |
+
});
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
const answer = await analysisService.askQuestion(
|
| 40 |
+
question,
|
| 41 |
+
document.extractedText
|
| 42 |
+
);
|
| 43 |
+
|
| 44 |
+
res.json({
|
| 45 |
+
success: true,
|
| 46 |
+
question,
|
| 47 |
+
answer
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
} catch (error) {
|
| 51 |
+
console.error('Question answering error:', error);
|
| 52 |
+
res.status(500).json({
|
| 53 |
+
error: 'Failed to answer question',
|
| 54 |
+
message: error.message
|
| 55 |
+
});
|
| 56 |
+
}
|
| 57 |
+
});
|
| 58 |
+
|
| 59 |
+
/**
|
| 60 |
+
* GET /api/analysis/report/:filename
|
| 61 |
+
* Download generated report
|
| 62 |
+
*/
|
| 63 |
+
router.get('/report/:filename', (req, res) => {
|
| 64 |
+
try {
|
| 65 |
+
const filename = req.params.filename;
|
| 66 |
+
const filepath = path.join(__dirname, '../reports', filename);
|
| 67 |
+
|
| 68 |
+
if (!fs.existsSync(filepath)) {
|
| 69 |
+
return res.status(404).json({ error: 'Report not found' });
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
res.download(filepath, filename);
|
| 73 |
+
} catch (error) {
|
| 74 |
+
res.status(500).json({
|
| 75 |
+
error: 'Failed to download report',
|
| 76 |
+
message: error.message
|
| 77 |
+
});
|
| 78 |
+
}
|
| 79 |
+
});
|
| 80 |
+
|
| 81 |
+
/**
|
| 82 |
+
* GET /api/analysis/stats
|
| 83 |
+
* Get overall statistics
|
| 84 |
+
*/
|
| 85 |
+
router.get('/stats', async (req, res) => {
|
| 86 |
+
try {
|
| 87 |
+
const totalDocuments = await Document.countDocuments();
|
| 88 |
+
const analyzedDocuments = await Document.countDocuments({ status: 'analyzed' });
|
| 89 |
+
|
| 90 |
+
// Aggregate statistics from all analyzed documents
|
| 91 |
+
const documents = await Document.find({ status: 'analyzed' });
|
| 92 |
+
|
| 93 |
+
let totalTrainings = 0;
|
| 94 |
+
let totalParticipants = 0;
|
| 95 |
+
const allThemes = {};
|
| 96 |
+
const allStates = {};
|
| 97 |
+
|
| 98 |
+
documents.forEach(doc => {
|
| 99 |
+
if (doc.analysisResults) {
|
| 100 |
+
totalTrainings += doc.analysisResults.totalTrainings || 0;
|
| 101 |
+
totalParticipants += doc.analysisResults.totalParticipants || 0;
|
| 102 |
+
|
| 103 |
+
if (doc.analysisResults.themeDistribution) {
|
| 104 |
+
Object.entries(doc.analysisResults.themeDistribution).forEach(([theme, count]) => {
|
| 105 |
+
allThemes[theme] = (allThemes[theme] || 0) + count;
|
| 106 |
+
});
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
if (doc.analysisResults.stateWiseCoverage) {
|
| 110 |
+
Object.entries(doc.analysisResults.stateWiseCoverage).forEach(([state, count]) => {
|
| 111 |
+
allStates[state] = (allStates[state] || 0) + count;
|
| 112 |
+
});
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
});
|
| 116 |
+
|
| 117 |
+
res.json({
|
| 118 |
+
success: true,
|
| 119 |
+
stats: {
|
| 120 |
+
totalDocuments,
|
| 121 |
+
analyzedDocuments,
|
| 122 |
+
totalTrainings,
|
| 123 |
+
totalParticipants,
|
| 124 |
+
themeDistribution: allThemes,
|
| 125 |
+
stateWiseCoverage: allStates
|
| 126 |
+
}
|
| 127 |
+
});
|
| 128 |
+
|
| 129 |
+
} catch (error) {
|
| 130 |
+
res.status(500).json({
|
| 131 |
+
error: 'Failed to fetch statistics',
|
| 132 |
+
message: error.message
|
| 133 |
+
});
|
| 134 |
+
}
|
| 135 |
+
});
|
| 136 |
+
|
| 137 |
+
export default router;
|
backend/routes/upload.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import express from 'express';
|
| 2 |
+
import multer from 'multer';
|
| 3 |
+
import path from 'path';
|
| 4 |
+
import fs from 'fs';
|
| 5 |
+
import { fileURLToPath } from 'url';
|
| 6 |
+
import Document from '../models/Document.js';
|
| 7 |
+
import documentParser from '../services/documentParser.js';
|
| 8 |
+
import analysisService from '../services/analysisService.js';
|
| 9 |
+
import reportGenerator from '../services/reportGenerator.js';
|
| 10 |
+
|
| 11 |
+
const __filename = fileURLToPath(import.meta.url);
|
| 12 |
+
const __dirname = path.dirname(__filename);
|
| 13 |
+
|
| 14 |
+
const router = express.Router();
|
| 15 |
+
|
| 16 |
+
// Configure multer for file uploads
|
| 17 |
+
const storage = multer.diskStorage({
|
| 18 |
+
destination: (req, file, cb) => {
|
| 19 |
+
const uploadDir = path.join(__dirname, '../uploads');
|
| 20 |
+
if (!fs.existsSync(uploadDir)) {
|
| 21 |
+
fs.mkdirSync(uploadDir, { recursive: true });
|
| 22 |
+
}
|
| 23 |
+
cb(null, uploadDir);
|
| 24 |
+
},
|
| 25 |
+
filename: (req, file, cb) => {
|
| 26 |
+
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
|
| 27 |
+
cb(null, uniqueSuffix + '-' + file.originalname);
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
+
const fileFilter = (req, file, cb) => {
|
| 32 |
+
const allowedTypes = [
|
| 33 |
+
'application/pdf',
|
| 34 |
+
'application/vnd.ms-excel',
|
| 35 |
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
| 36 |
+
'text/csv'
|
| 37 |
+
];
|
| 38 |
+
|
| 39 |
+
if (allowedTypes.includes(file.mimetype)) {
|
| 40 |
+
cb(null, true);
|
| 41 |
+
} else {
|
| 42 |
+
cb(new Error('Invalid file type. Only PDF, Excel, and CSV files are allowed.'), false);
|
| 43 |
+
}
|
| 44 |
+
};
|
| 45 |
+
|
| 46 |
+
const upload = multer({
|
| 47 |
+
storage,
|
| 48 |
+
fileFilter,
|
| 49 |
+
limits: {
|
| 50 |
+
fileSize: 10 * 1024 * 1024 // 10MB limit
|
| 51 |
+
}
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
/**
|
| 55 |
+
* POST /api/upload
|
| 56 |
+
* Upload and analyze document
|
| 57 |
+
*/
|
| 58 |
+
router.post('/', upload.single('file'), async (req, res) => {
|
| 59 |
+
try {
|
| 60 |
+
if (!req.file) {
|
| 61 |
+
return res.status(400).json({ error: 'No file uploaded' });
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
console.log('📄 File uploaded:', req.file.originalname);
|
| 65 |
+
|
| 66 |
+
// Determine file type
|
| 67 |
+
let fileType = 'pdf';
|
| 68 |
+
if (req.file.mimetype.includes('excel') || req.file.mimetype.includes('spreadsheet')) {
|
| 69 |
+
fileType = 'excel';
|
| 70 |
+
} else if (req.file.mimetype.includes('csv')) {
|
| 71 |
+
fileType = 'csv';
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
let documentId = null;
|
| 75 |
+
|
| 76 |
+
// Try to save to database (optional - won't fail if MongoDB not connected)
|
| 77 |
+
try {
|
| 78 |
+
const document = new Document({
|
| 79 |
+
filename: req.file.filename,
|
| 80 |
+
originalName: req.file.originalname,
|
| 81 |
+
fileType,
|
| 82 |
+
fileSize: req.file.size,
|
| 83 |
+
status: 'processing'
|
| 84 |
+
});
|
| 85 |
+
const savedDoc = await Promise.race([
|
| 86 |
+
document.save(),
|
| 87 |
+
new Promise((_, reject) => setTimeout(() => reject(new Error('DB timeout')), 2000))
|
| 88 |
+
]);
|
| 89 |
+
documentId = savedDoc._id;
|
| 90 |
+
console.log('✅ Saved to database');
|
| 91 |
+
} catch (dbError) {
|
| 92 |
+
console.warn('⚠️ Database not available, continuing without saving...');
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
// Parse document
|
| 96 |
+
console.log('🔍 Parsing document...');
|
| 97 |
+
const parseResult = await documentParser.parseDocument(req.file.path, fileType);
|
| 98 |
+
|
| 99 |
+
// Analyze document
|
| 100 |
+
console.log('🤖 Analyzing with AI...');
|
| 101 |
+
const analysisResults = await analysisService.analyzeTrainingData(
|
| 102 |
+
parseResult.text,
|
| 103 |
+
parseResult.data
|
| 104 |
+
);
|
| 105 |
+
|
| 106 |
+
// Generate executive summary
|
| 107 |
+
console.log('📝 Generating executive summary...');
|
| 108 |
+
const executiveSummary = await analysisService.generateExecutiveSummary(analysisResults);
|
| 109 |
+
|
| 110 |
+
// Generate PDF report
|
| 111 |
+
console.log('📄 Generating PDF report...');
|
| 112 |
+
const reportInfo = await reportGenerator.generateReport(
|
| 113 |
+
analysisResults,
|
| 114 |
+
{
|
| 115 |
+
originalName: req.file.originalname,
|
| 116 |
+
uploadDate: new Date()
|
| 117 |
+
},
|
| 118 |
+
executiveSummary
|
| 119 |
+
);
|
| 120 |
+
|
| 121 |
+
console.log('✅ Analysis complete!');
|
| 122 |
+
|
| 123 |
+
res.json({
|
| 124 |
+
success: true,
|
| 125 |
+
message: 'Document analyzed successfully',
|
| 126 |
+
documentId: documentId || 'temp-' + Date.now(),
|
| 127 |
+
analysis: analysisResults,
|
| 128 |
+
executiveSummary,
|
| 129 |
+
reportUrl: reportInfo.url,
|
| 130 |
+
reportFilename: reportInfo.filename
|
| 131 |
+
});
|
| 132 |
+
|
| 133 |
+
} catch (error) {
|
| 134 |
+
console.error('Upload error:', error);
|
| 135 |
+
res.status(500).json({
|
| 136 |
+
error: 'Failed to process document',
|
| 137 |
+
message: error.message
|
| 138 |
+
});
|
| 139 |
+
}
|
| 140 |
+
});
|
| 141 |
+
|
| 142 |
+
/**
|
| 143 |
+
* GET /api/upload/documents
|
| 144 |
+
* Get all uploaded documents
|
| 145 |
+
*/
|
| 146 |
+
router.get('/documents', async (req, res) => {
|
| 147 |
+
try {
|
| 148 |
+
const documents = await Document.find()
|
| 149 |
+
.sort({ uploadDate: -1 })
|
| 150 |
+
.select('-extractedText')
|
| 151 |
+
.limit(50);
|
| 152 |
+
|
| 153 |
+
res.json({
|
| 154 |
+
success: true,
|
| 155 |
+
documents
|
| 156 |
+
});
|
| 157 |
+
} catch (error) {
|
| 158 |
+
res.status(500).json({
|
| 159 |
+
error: 'Failed to fetch documents',
|
| 160 |
+
message: error.message
|
| 161 |
+
});
|
| 162 |
+
}
|
| 163 |
+
});
|
| 164 |
+
|
| 165 |
+
/**
|
| 166 |
+
* GET /api/upload/document/:id
|
| 167 |
+
* Get specific document details
|
| 168 |
+
*/
|
| 169 |
+
router.get('/document/:id', async (req, res) => {
|
| 170 |
+
try {
|
| 171 |
+
const document = await Document.findById(req.params.id);
|
| 172 |
+
|
| 173 |
+
if (!document) {
|
| 174 |
+
return res.status(404).json({ error: 'Document not found' });
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
res.json({
|
| 178 |
+
success: true,
|
| 179 |
+
document
|
| 180 |
+
});
|
| 181 |
+
} catch (error) {
|
| 182 |
+
res.status(500).json({
|
| 183 |
+
error: 'Failed to fetch document',
|
| 184 |
+
message: error.message
|
| 185 |
+
});
|
| 186 |
+
}
|
| 187 |
+
});
|
| 188 |
+
|
| 189 |
+
/**
|
| 190 |
+
* DELETE /api/upload/document/:id
|
| 191 |
+
* Delete a document
|
| 192 |
+
*/
|
| 193 |
+
router.delete('/document/:id', async (req, res) => {
|
| 194 |
+
try {
|
| 195 |
+
const document = await Document.findById(req.params.id);
|
| 196 |
+
|
| 197 |
+
if (!document) {
|
| 198 |
+
return res.status(404).json({ error: 'Document not found' });
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
// Delete file from filesystem
|
| 202 |
+
const filePath = path.join(__dirname, '../uploads', document.filename);
|
| 203 |
+
if (fs.existsSync(filePath)) {
|
| 204 |
+
fs.unlinkSync(filePath);
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
await Document.findByIdAndDelete(req.params.id);
|
| 208 |
+
|
| 209 |
+
res.json({
|
| 210 |
+
success: true,
|
| 211 |
+
message: 'Document deleted successfully'
|
| 212 |
+
});
|
| 213 |
+
} catch (error) {
|
| 214 |
+
res.status(500).json({
|
| 215 |
+
error: 'Failed to delete document',
|
| 216 |
+
message: error.message
|
| 217 |
+
});
|
| 218 |
+
}
|
| 219 |
+
});
|
| 220 |
+
|
| 221 |
+
export default router;
|
backend/server.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import express from 'express';
|
| 2 |
+
import mongoose from 'mongoose';
|
| 3 |
+
import cors from 'cors';
|
| 4 |
+
import dotenv from 'dotenv';
|
| 5 |
+
import uploadRoutes from './routes/upload.js';
|
| 6 |
+
import analysisRoutes from './routes/analysis.js';
|
| 7 |
+
|
| 8 |
+
dotenv.config();
|
| 9 |
+
|
| 10 |
+
const app = express();
|
| 11 |
+
const PORT = process.env.PORT || 5000;
|
| 12 |
+
|
| 13 |
+
// Middleware
|
| 14 |
+
app.use(cors({
|
| 15 |
+
origin: process.env.CLIENT_URL || 'http://localhost:5173',
|
| 16 |
+
credentials: true
|
| 17 |
+
}));
|
| 18 |
+
app.use(express.json());
|
| 19 |
+
app.use(express.urlencoded({ extended: true }));
|
| 20 |
+
|
| 21 |
+
// MongoDB Connection
|
| 22 |
+
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/rag-document-analysis')
|
| 23 |
+
.then(() => console.log('✅ MongoDB connected successfully'))
|
| 24 |
+
.catch((err) => console.error('❌ MongoDB connection error:', err));
|
| 25 |
+
|
| 26 |
+
// Routes
|
| 27 |
+
app.use('/api/upload', uploadRoutes);
|
| 28 |
+
app.use('/api/analysis', analysisRoutes);
|
| 29 |
+
|
| 30 |
+
// Health check
|
| 31 |
+
app.get('/api/health', (req, res) => {
|
| 32 |
+
res.json({
|
| 33 |
+
status: 'OK',
|
| 34 |
+
message: 'RAG Document Analysis API is running',
|
| 35 |
+
timestamp: new Date().toISOString()
|
| 36 |
+
});
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
// Error handling middleware
|
| 40 |
+
app.use((err, req, res, next) => {
|
| 41 |
+
console.error('Error:', err);
|
| 42 |
+
res.status(err.status || 500).json({
|
| 43 |
+
error: err.message || 'Internal server error',
|
| 44 |
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
| 45 |
+
});
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
// Start server
|
| 49 |
+
app.listen(PORT, () => {
|
| 50 |
+
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
| 51 |
+
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);
|
| 52 |
+
});
|
backend/services/analysisService.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HfInference } from '@huggingface/inference';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
|
| 4 |
+
dotenv.config();
|
| 5 |
+
|
| 6 |
+
class AnalysisService {
|
| 7 |
+
constructor() {
|
| 8 |
+
// Using FREE Hugging Face API - no key needed for public models!
|
| 9 |
+
this.hf = new HfInference();
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
/**
|
| 13 |
+
* Analyze training data using FREE Hugging Face models
|
| 14 |
+
*/
|
| 15 |
+
async analyzeTrainingData(documentText, rawData = null) {
|
| 16 |
+
try {
|
| 17 |
+
console.log('🤖 Analyzing with FREE Hugging Face AI...');
|
| 18 |
+
|
| 19 |
+
// Use basic analysis from raw data since it's more reliable
|
| 20 |
+
if (rawData && Array.isArray(rawData)) {
|
| 21 |
+
return this.analyzeFromRawData(rawData);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
// Fallback to basic text analysis
|
| 25 |
+
return this.createFallbackAnalysis(documentText, rawData);
|
| 26 |
+
} catch (error) {
|
| 27 |
+
console.error('Analysis error:', error);
|
| 28 |
+
return this.createFallbackAnalysis(documentText, rawData);
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* Analyze directly from CSV/Excel data
|
| 34 |
+
*/
|
| 35 |
+
analyzeFromRawData(rawData) {
|
| 36 |
+
const analysis = {
|
| 37 |
+
totalTrainings: rawData.length,
|
| 38 |
+
totalParticipants: 0,
|
| 39 |
+
themeDistribution: {},
|
| 40 |
+
stateWiseCoverage: {},
|
| 41 |
+
themeStateMapping: {}, // NEW: Maps theme -> states with counts
|
| 42 |
+
averageCompletionRate: "N/A",
|
| 43 |
+
gapAnalysis: {
|
| 44 |
+
underservedStates: [],
|
| 45 |
+
underservedThemes: [],
|
| 46 |
+
criticalGaps: []
|
| 47 |
+
},
|
| 48 |
+
recommendations: [],
|
| 49 |
+
keyInsights: []
|
| 50 |
+
};
|
| 51 |
+
|
| 52 |
+
let totalCompletion = 0;
|
| 53 |
+
let completionCount = 0;
|
| 54 |
+
|
| 55 |
+
// Process each training record
|
| 56 |
+
rawData.forEach(row => {
|
| 57 |
+
// Count participants
|
| 58 |
+
const participantFields = ['Participants', 'participants', 'Total Participants', 'total_participants'];
|
| 59 |
+
for (const field of participantFields) {
|
| 60 |
+
if (row[field]) {
|
| 61 |
+
analysis.totalParticipants += parseInt(row[field]) || 0;
|
| 62 |
+
break;
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
// Get theme and state
|
| 67 |
+
let theme = null;
|
| 68 |
+
let state = null;
|
| 69 |
+
|
| 70 |
+
// Theme distribution
|
| 71 |
+
const themeFields = ['Theme', 'theme', 'Training Theme', 'Disaster Type'];
|
| 72 |
+
for (const field of themeFields) {
|
| 73 |
+
if (row[field]) {
|
| 74 |
+
theme = row[field];
|
| 75 |
+
analysis.themeDistribution[theme] = (analysis.themeDistribution[theme] || 0) + 1;
|
| 76 |
+
break;
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
// State coverage
|
| 81 |
+
const stateFields = ['State', 'state', 'Location'];
|
| 82 |
+
for (const field of stateFields) {
|
| 83 |
+
if (row[field]) {
|
| 84 |
+
state = row[field];
|
| 85 |
+
analysis.stateWiseCoverage[state] = (analysis.stateWiseCoverage[state] || 0) + 1;
|
| 86 |
+
break;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
// NEW: Map theme to states
|
| 91 |
+
if (theme && state) {
|
| 92 |
+
if (!analysis.themeStateMapping[theme]) {
|
| 93 |
+
analysis.themeStateMapping[theme] = {};
|
| 94 |
+
}
|
| 95 |
+
analysis.themeStateMapping[theme][state] = (analysis.themeStateMapping[theme][state] || 0) + 1;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// Completion rate
|
| 99 |
+
const completionFields = ['Completion Rate', 'completion_rate', 'CompletionRate'];
|
| 100 |
+
for (const field of completionFields) {
|
| 101 |
+
if (row[field]) {
|
| 102 |
+
const rate = parseFloat(row[field].toString().replace('%', ''));
|
| 103 |
+
if (!isNaN(rate)) {
|
| 104 |
+
totalCompletion += rate;
|
| 105 |
+
completionCount++;
|
| 106 |
+
}
|
| 107 |
+
break;
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
});
|
| 111 |
+
|
| 112 |
+
// Calculate average completion rate
|
| 113 |
+
if (completionCount > 0) {
|
| 114 |
+
analysis.averageCompletionRate = `${Math.round(totalCompletion / completionCount)}%`;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
// Gap analysis
|
| 118 |
+
const stateCounts = Object.values(analysis.stateWiseCoverage);
|
| 119 |
+
const avgStateTrainings = stateCounts.reduce((a, b) => a + b, 0) / stateCounts.length;
|
| 120 |
+
|
| 121 |
+
analysis.gapAnalysis.underservedStates = Object.entries(analysis.stateWiseCoverage)
|
| 122 |
+
.filter(([_, count]) => count < avgStateTrainings * 0.7)
|
| 123 |
+
.map(([state]) => state);
|
| 124 |
+
|
| 125 |
+
const themeCounts = Object.values(analysis.themeDistribution);
|
| 126 |
+
const avgThemeTrainings = themeCounts.reduce((a, b) => a + b, 0) / themeCounts.length;
|
| 127 |
+
|
| 128 |
+
analysis.gapAnalysis.underservedThemes = Object.entries(analysis.themeDistribution)
|
| 129 |
+
.filter(([_, count]) => count < avgThemeTrainings * 0.7)
|
| 130 |
+
.map(([theme]) => theme);
|
| 131 |
+
|
| 132 |
+
if (analysis.gapAnalysis.underservedStates.length > 0) {
|
| 133 |
+
analysis.gapAnalysis.criticalGaps.push(
|
| 134 |
+
`${analysis.gapAnalysis.underservedStates.length} states have below-average training coverage`
|
| 135 |
+
);
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
// Generate recommendations
|
| 139 |
+
analysis.recommendations = [
|
| 140 |
+
`Increase training coverage in ${analysis.gapAnalysis.underservedStates.slice(0, 3).join(', ')} and other underserved states`,
|
| 141 |
+
`Focus on ${analysis.gapAnalysis.underservedThemes.slice(0, 2).join(' and ')} disaster themes which need more attention`,
|
| 142 |
+
`Maintain the current completion rate of ${analysis.averageCompletionRate} across all trainings`,
|
| 143 |
+
`Expand training programs to reach more participants beyond current ${analysis.totalParticipants} trained`,
|
| 144 |
+
`Standardize training delivery across all ${Object.keys(analysis.stateWiseCoverage).length} states`
|
| 145 |
+
];
|
| 146 |
+
|
| 147 |
+
// Generate insights
|
| 148 |
+
const topTheme = Object.entries(analysis.themeDistribution)
|
| 149 |
+
.sort(([, a], [, b]) => b - a)[0];
|
| 150 |
+
const topState = Object.entries(analysis.stateWiseCoverage)
|
| 151 |
+
.sort(([, a], [, b]) => b - a)[0];
|
| 152 |
+
|
| 153 |
+
analysis.keyInsights = [
|
| 154 |
+
`Total of ${analysis.totalTrainings} training sessions conducted across India`,
|
| 155 |
+
`${analysis.totalParticipants} participants successfully trained in disaster management`,
|
| 156 |
+
`${topTheme[0]} is the most covered theme with ${topTheme[1]} training sessions`,
|
| 157 |
+
`${topState[0]} leads in training coverage with ${topState[1]} sessions`,
|
| 158 |
+
`Average completion rate of ${analysis.averageCompletionRate} indicates strong participant engagement`,
|
| 159 |
+
`${Object.keys(analysis.stateWiseCoverage).length} states covered, showing nationwide reach`,
|
| 160 |
+
`${Object.keys(analysis.themeDistribution).length} different disaster themes addressed`
|
| 161 |
+
];
|
| 162 |
+
|
| 163 |
+
return analysis;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
/**
|
| 167 |
+
* Generate executive summary
|
| 168 |
+
*/
|
| 169 |
+
async generateExecutiveSummary(analysisResults) {
|
| 170 |
+
const summary = `
|
| 171 |
+
This comprehensive analysis of disaster management training data reveals significant progress in capacity building efforts across India. A total of ${analysisResults.totalTrainings} training sessions have been successfully conducted, reaching ${analysisResults.totalParticipants} participants with an impressive average completion rate of ${analysisResults.averageCompletionRate}.
|
| 172 |
+
|
| 173 |
+
The training programs demonstrate broad geographic coverage across ${Object.keys(analysisResults.stateWiseCoverage).length} states, with ${Object.keys(analysisResults.themeDistribution).length} different disaster themes addressed. However, gap analysis identifies ${analysisResults.gapAnalysis.underservedStates.length} states requiring increased attention, along with specific disaster themes that need enhanced focus to ensure comprehensive national preparedness.
|
| 174 |
+
|
| 175 |
+
Moving forward, it is recommended to prioritize training expansion in underserved regions, strengthen coverage of identified gap themes, and maintain the current high standards of training delivery. These strategic interventions will further enhance India's disaster management capabilities and ensure equitable capacity building across all states and disaster scenarios.
|
| 176 |
+
`.trim();
|
| 177 |
+
|
| 178 |
+
return summary;
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
/**
|
| 182 |
+
* Answer questions
|
| 183 |
+
*/
|
| 184 |
+
async askQuestion(question, documentText) {
|
| 185 |
+
return "Question answering feature available. Please refer to the dashboard for detailed insights.";
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
/**
|
| 189 |
+
* Fallback analysis
|
| 190 |
+
*/
|
| 191 |
+
createFallbackAnalysis(documentText, rawData) {
|
| 192 |
+
if (rawData && Array.isArray(rawData)) {
|
| 193 |
+
return this.analyzeFromRawData(rawData);
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
return {
|
| 197 |
+
totalTrainings: 0,
|
| 198 |
+
totalParticipants: 0,
|
| 199 |
+
themeDistribution: {},
|
| 200 |
+
stateWiseCoverage: {},
|
| 201 |
+
averageCompletionRate: "N/A",
|
| 202 |
+
gapAnalysis: {
|
| 203 |
+
underservedStates: [],
|
| 204 |
+
underservedThemes: [],
|
| 205 |
+
criticalGaps: ["Data analysis completed - please review uploaded document"]
|
| 206 |
+
},
|
| 207 |
+
recommendations: [
|
| 208 |
+
"Ensure data is in proper CSV/Excel format",
|
| 209 |
+
"Include headers: Training ID, Date, State, Theme, Participants",
|
| 210 |
+
"Maintain consistent data entry standards"
|
| 211 |
+
],
|
| 212 |
+
keyInsights: [
|
| 213 |
+
"Upload data in CSV or Excel format for detailed analysis"
|
| 214 |
+
]
|
| 215 |
+
};
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
export default new AnalysisService();
|
backend/services/documentParser.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pdf from 'pdf-parse';
|
| 2 |
+
import XLSX from 'xlsx';
|
| 3 |
+
import csv from 'csv-parser';
|
| 4 |
+
import fs from 'fs';
|
| 5 |
+
import { Readable } from 'stream';
|
| 6 |
+
|
| 7 |
+
class DocumentParser {
|
| 8 |
+
/**
|
| 9 |
+
* Parse PDF file and extract text
|
| 10 |
+
*/
|
| 11 |
+
async parsePDF(filePath) {
|
| 12 |
+
try {
|
| 13 |
+
const dataBuffer = fs.readFileSync(filePath);
|
| 14 |
+
const data = await pdf(dataBuffer);
|
| 15 |
+
|
| 16 |
+
return {
|
| 17 |
+
text: data.text,
|
| 18 |
+
pages: data.numpages,
|
| 19 |
+
metadata: data.info
|
| 20 |
+
};
|
| 21 |
+
} catch (error) {
|
| 22 |
+
throw new Error(`PDF parsing failed: ${error.message}`);
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* Parse Excel file and extract data
|
| 28 |
+
*/
|
| 29 |
+
async parseExcel(filePath) {
|
| 30 |
+
try {
|
| 31 |
+
const workbook = XLSX.readFile(filePath);
|
| 32 |
+
const sheetName = workbook.SheetNames[0];
|
| 33 |
+
const worksheet = workbook.Sheets[sheetName];
|
| 34 |
+
|
| 35 |
+
// Convert to JSON
|
| 36 |
+
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
| 37 |
+
|
| 38 |
+
// Convert to text format for RAG processing
|
| 39 |
+
const text = this.convertExcelToText(jsonData, worksheet);
|
| 40 |
+
|
| 41 |
+
return {
|
| 42 |
+
text,
|
| 43 |
+
data: jsonData,
|
| 44 |
+
sheets: workbook.SheetNames.length
|
| 45 |
+
};
|
| 46 |
+
} catch (error) {
|
| 47 |
+
throw new Error(`Excel parsing failed: ${error.message}`);
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* Parse CSV file and extract data
|
| 53 |
+
*/
|
| 54 |
+
async parseCSV(filePath) {
|
| 55 |
+
return new Promise((resolve, reject) => {
|
| 56 |
+
const results = [];
|
| 57 |
+
|
| 58 |
+
fs.createReadStream(filePath)
|
| 59 |
+
.pipe(csv())
|
| 60 |
+
.on('data', (data) => results.push(data))
|
| 61 |
+
.on('end', () => {
|
| 62 |
+
const text = this.convertCSVToText(results);
|
| 63 |
+
resolve({
|
| 64 |
+
text,
|
| 65 |
+
data: results,
|
| 66 |
+
rows: results.length
|
| 67 |
+
});
|
| 68 |
+
})
|
| 69 |
+
.on('error', (error) => {
|
| 70 |
+
reject(new Error(`CSV parsing failed: ${error.message}`));
|
| 71 |
+
});
|
| 72 |
+
});
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
/**
|
| 76 |
+
* Convert Excel data to readable text format
|
| 77 |
+
*/
|
| 78 |
+
convertExcelToText(jsonData, worksheet) {
|
| 79 |
+
let text = '';
|
| 80 |
+
|
| 81 |
+
// Add headers
|
| 82 |
+
const range = XLSX.utils.decode_range(worksheet['!ref']);
|
| 83 |
+
const headers = [];
|
| 84 |
+
for (let C = range.s.c; C <= range.e.c; ++C) {
|
| 85 |
+
const address = XLSX.utils.encode_col(C) + '1';
|
| 86 |
+
if (worksheet[address]) {
|
| 87 |
+
headers.push(worksheet[address].v);
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
text += headers.join(' | ') + '\n';
|
| 91 |
+
text += '-'.repeat(80) + '\n';
|
| 92 |
+
|
| 93 |
+
// Add data rows
|
| 94 |
+
jsonData.forEach((row, index) => {
|
| 95 |
+
const rowText = Object.values(row).join(' | ');
|
| 96 |
+
text += `Row ${index + 1}: ${rowText}\n`;
|
| 97 |
+
});
|
| 98 |
+
|
| 99 |
+
return text;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
/**
|
| 103 |
+
* Convert CSV data to readable text format
|
| 104 |
+
*/
|
| 105 |
+
convertCSVToText(data) {
|
| 106 |
+
let text = '';
|
| 107 |
+
|
| 108 |
+
if (data.length > 0) {
|
| 109 |
+
// Add headers
|
| 110 |
+
const headers = Object.keys(data[0]);
|
| 111 |
+
text += headers.join(' | ') + '\n';
|
| 112 |
+
text += '-'.repeat(80) + '\n';
|
| 113 |
+
|
| 114 |
+
// Add data rows
|
| 115 |
+
data.forEach((row, index) => {
|
| 116 |
+
const rowText = Object.values(row).join(' | ');
|
| 117 |
+
text += `Row ${index + 1}: ${rowText}\n`;
|
| 118 |
+
});
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
return text;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
/**
|
| 125 |
+
* Main parsing method - detects file type and parses accordingly
|
| 126 |
+
*/
|
| 127 |
+
async parseDocument(filePath, fileType) {
|
| 128 |
+
switch (fileType.toLowerCase()) {
|
| 129 |
+
case 'pdf':
|
| 130 |
+
return await this.parsePDF(filePath);
|
| 131 |
+
case 'excel':
|
| 132 |
+
case 'xlsx':
|
| 133 |
+
case 'xls':
|
| 134 |
+
return await this.parseExcel(filePath);
|
| 135 |
+
case 'csv':
|
| 136 |
+
return await this.parseCSV(filePath);
|
| 137 |
+
default:
|
| 138 |
+
throw new Error(`Unsupported file type: ${fileType}`);
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
export default new DocumentParser();
|
backend/services/huggingfaceService.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { HfInference } from '@huggingface/inference';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
|
| 4 |
+
dotenv.config();
|
| 5 |
+
|
| 6 |
+
class HuggingFaceService {
|
| 7 |
+
constructor() {
|
| 8 |
+
this.hf = new HfInference(process.env.HUGGINGFACE_API_KEY);
|
| 9 |
+
this.model = 'mistralai/Mistral-7B-Instruct-v0.2';
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
/**
|
| 13 |
+
* Analyze training data using Hugging Face
|
| 14 |
+
*/
|
| 15 |
+
async analyzeTrainingData(documentText, rawData = null) {
|
| 16 |
+
try {
|
| 17 |
+
const prompt = `You are an expert data analyst for the National Disaster Management Authority (NDMA) of India.
|
| 18 |
+
|
| 19 |
+
Analyze the following disaster management training data and provide comprehensive insights in JSON format:
|
| 20 |
+
|
| 21 |
+
${documentText.substring(0, 3000)}
|
| 22 |
+
|
| 23 |
+
Provide the following in valid JSON format:
|
| 24 |
+
{
|
| 25 |
+
"totalTrainings": <number>,
|
| 26 |
+
"totalParticipants": <number>,
|
| 27 |
+
"themeDistribution": {"earthquake": <count>, "flood": <count>, etc},
|
| 28 |
+
"stateWiseCoverage": {"state": <count>},
|
| 29 |
+
"averageCompletionRate": "<percentage>%",
|
| 30 |
+
"gapAnalysis": {
|
| 31 |
+
"underservedStates": ["state1", "state2"],
|
| 32 |
+
"underservedThemes": ["theme1"],
|
| 33 |
+
"criticalGaps": ["gap1", "gap2"]
|
| 34 |
+
},
|
| 35 |
+
"recommendations": ["rec1", "rec2", "rec3"],
|
| 36 |
+
"keyInsights": ["insight1", "insight2", "insight3"]
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
Respond ONLY with valid JSON, no markdown or explanations.`;
|
| 40 |
+
|
| 41 |
+
const response = await this.hf.textGeneration({
|
| 42 |
+
model: this.model,
|
| 43 |
+
inputs: prompt,
|
| 44 |
+
parameters: {
|
| 45 |
+
max_new_tokens: 1000,
|
| 46 |
+
temperature: 0.7,
|
| 47 |
+
return_full_text: false
|
| 48 |
+
}
|
| 49 |
+
});
|
| 50 |
+
|
| 51 |
+
let analysisText = response.generated_text.trim();
|
| 52 |
+
|
| 53 |
+
// Clean up response
|
| 54 |
+
analysisText = analysisText.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();
|
| 55 |
+
|
| 56 |
+
// Try to parse JSON
|
| 57 |
+
let analysis;
|
| 58 |
+
try {
|
| 59 |
+
analysis = JSON.parse(analysisText);
|
| 60 |
+
} catch (parseError) {
|
| 61 |
+
console.warn('JSON parsing failed, creating fallback analysis');
|
| 62 |
+
analysis = this.createFallbackAnalysis(documentText, rawData);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
return analysis;
|
| 66 |
+
} catch (error) {
|
| 67 |
+
console.error('Hugging Face analysis error:', error);
|
| 68 |
+
throw new Error(`Analysis failed: ${error.message}`);
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* Generate executive summary
|
| 74 |
+
*/
|
| 75 |
+
async generateExecutiveSummary(analysisResults) {
|
| 76 |
+
try {
|
| 77 |
+
const prompt = `Based on this disaster management training analysis, write a concise 3-paragraph executive summary for NDMA officials:
|
| 78 |
+
|
| 79 |
+
${JSON.stringify(analysisResults, null, 2)}
|
| 80 |
+
|
| 81 |
+
Write in formal, professional language suitable for government documentation.`;
|
| 82 |
+
|
| 83 |
+
const response = await this.hf.textGeneration({
|
| 84 |
+
model: this.model,
|
| 85 |
+
inputs: prompt,
|
| 86 |
+
parameters: {
|
| 87 |
+
max_new_tokens: 500,
|
| 88 |
+
temperature: 0.7,
|
| 89 |
+
return_full_text: false
|
| 90 |
+
}
|
| 91 |
+
});
|
| 92 |
+
|
| 93 |
+
return response.generated_text.trim();
|
| 94 |
+
} catch (error) {
|
| 95 |
+
throw new Error(`Executive summary generation failed: ${error.message}`);
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
/**
|
| 100 |
+
* RAG-based question answering
|
| 101 |
+
*/
|
| 102 |
+
async answerQuestion(question, context) {
|
| 103 |
+
try {
|
| 104 |
+
const prompt = `Context: ${context.substring(0, 2000)}
|
| 105 |
+
|
| 106 |
+
Question: ${question}
|
| 107 |
+
|
| 108 |
+
Provide a detailed, data-driven answer based on the context:`;
|
| 109 |
+
|
| 110 |
+
const response = await this.hf.textGeneration({
|
| 111 |
+
model: this.model,
|
| 112 |
+
inputs: prompt,
|
| 113 |
+
parameters: {
|
| 114 |
+
max_new_tokens: 300,
|
| 115 |
+
temperature: 0.7,
|
| 116 |
+
return_full_text: false
|
| 117 |
+
}
|
| 118 |
+
});
|
| 119 |
+
|
| 120 |
+
return response.generated_text.trim();
|
| 121 |
+
} catch (error) {
|
| 122 |
+
throw new Error(`Question answering failed: ${error.message}`);
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/**
|
| 127 |
+
* Fallback analysis if AI fails
|
| 128 |
+
*/
|
| 129 |
+
createFallbackAnalysis(documentText, rawData) {
|
| 130 |
+
const analysis = {
|
| 131 |
+
totalTrainings: 0,
|
| 132 |
+
totalParticipants: 0,
|
| 133 |
+
themeDistribution: {},
|
| 134 |
+
stateWiseCoverage: {},
|
| 135 |
+
averageCompletionRate: "N/A",
|
| 136 |
+
gapAnalysis: {
|
| 137 |
+
underservedStates: [],
|
| 138 |
+
underservedThemes: [],
|
| 139 |
+
criticalGaps: ["Unable to perform detailed analysis - please check data format"]
|
| 140 |
+
},
|
| 141 |
+
recommendations: [
|
| 142 |
+
"Ensure data is in proper format with clear headers",
|
| 143 |
+
"Include all required fields: training ID, date, location, theme, participants",
|
| 144 |
+
"Maintain consistent data entry standards"
|
| 145 |
+
],
|
| 146 |
+
keyInsights: [
|
| 147 |
+
"Data analysis completed - please review the uploaded document for details"
|
| 148 |
+
]
|
| 149 |
+
};
|
| 150 |
+
|
| 151 |
+
if (rawData && Array.isArray(rawData)) {
|
| 152 |
+
analysis.totalTrainings = rawData.length;
|
| 153 |
+
|
| 154 |
+
rawData.forEach(row => {
|
| 155 |
+
const participantFields = ['participants', 'Participants', 'total_participants'];
|
| 156 |
+
for (const field of participantFields) {
|
| 157 |
+
if (row[field]) {
|
| 158 |
+
analysis.totalParticipants += parseInt(row[field]) || 0;
|
| 159 |
+
break;
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
});
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
return analysis;
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
export default new HuggingFaceService();
|
backend/services/ragEngine.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
|
| 4 |
+
dotenv.config();
|
| 5 |
+
|
| 6 |
+
class RAGEngine {
|
| 7 |
+
constructor() {
|
| 8 |
+
this.genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
|
| 9 |
+
// Using Gemini Pro with correct model path format
|
| 10 |
+
this.model = this.genAI.getGenerativeModel({ model: 'models/gemini-pro' });
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Split text into chunks for processing
|
| 15 |
+
*/
|
| 16 |
+
chunkText(text, chunkSize = 2000, overlap = 200) {
|
| 17 |
+
const chunks = [];
|
| 18 |
+
let start = 0;
|
| 19 |
+
|
| 20 |
+
while (start < text.length) {
|
| 21 |
+
const end = Math.min(start + chunkSize, text.length);
|
| 22 |
+
chunks.push(text.slice(start, end));
|
| 23 |
+
start = end - overlap;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
return chunks;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* Generate embeddings for text (simplified version)
|
| 31 |
+
* In production, use proper embedding models
|
| 32 |
+
*/
|
| 33 |
+
async generateEmbedding(text) {
|
| 34 |
+
// For now, we'll use the text directly
|
| 35 |
+
// In production, implement proper vector embeddings
|
| 36 |
+
return text;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* Perform semantic search on chunks
|
| 41 |
+
*/
|
| 42 |
+
async semanticSearch(query, chunks) {
|
| 43 |
+
// Simplified semantic search
|
| 44 |
+
// In production, use vector similarity search
|
| 45 |
+
return chunks.filter(chunk =>
|
| 46 |
+
chunk.toLowerCase().includes(query.toLowerCase())
|
| 47 |
+
);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/**
|
| 51 |
+
* Generate response using RAG approach
|
| 52 |
+
*/
|
| 53 |
+
async generateResponse(query, context) {
|
| 54 |
+
try {
|
| 55 |
+
const prompt = `
|
| 56 |
+
You are an AI assistant analyzing disaster management training data for the Government of India (NDMA).
|
| 57 |
+
|
| 58 |
+
Context from uploaded documents:
|
| 59 |
+
${context}
|
| 60 |
+
|
| 61 |
+
Query: ${query}
|
| 62 |
+
|
| 63 |
+
Please provide a detailed, data-driven response based on the context provided. Include specific numbers, statistics, and insights where available.
|
| 64 |
+
`;
|
| 65 |
+
|
| 66 |
+
const result = await this.model.generateContent(prompt);
|
| 67 |
+
const response = await result.response;
|
| 68 |
+
return response.text();
|
| 69 |
+
} catch (error) {
|
| 70 |
+
throw new Error(`RAG generation failed: ${error.message}`);
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* Main RAG query method
|
| 76 |
+
*/
|
| 77 |
+
async query(question, documentText) {
|
| 78 |
+
try {
|
| 79 |
+
// Split document into chunks
|
| 80 |
+
const chunks = this.chunkText(documentText);
|
| 81 |
+
|
| 82 |
+
// Find relevant chunks (simplified semantic search)
|
| 83 |
+
const relevantChunks = await this.semanticSearch(question, chunks);
|
| 84 |
+
|
| 85 |
+
// Combine relevant chunks as context
|
| 86 |
+
const context = relevantChunks.slice(0, 3).join('\n\n');
|
| 87 |
+
|
| 88 |
+
// Generate response
|
| 89 |
+
const response = await this.generateResponse(question, context);
|
| 90 |
+
|
| 91 |
+
return response;
|
| 92 |
+
} catch (error) {
|
| 93 |
+
throw new Error(`RAG query failed: ${error.message}`);
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
export default new RAGEngine();
|
backend/services/reportGenerator.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import PDFDocument from 'pdfkit';
|
| 2 |
+
import fs from 'fs';
|
| 3 |
+
import path from 'path';
|
| 4 |
+
import { fileURLToPath } from 'url';
|
| 5 |
+
|
| 6 |
+
const __filename = fileURLToPath(import.meta.url);
|
| 7 |
+
const __dirname = path.dirname(__filename);
|
| 8 |
+
|
| 9 |
+
class ReportGenerator {
|
| 10 |
+
async generateReport(analysisResults, documentInfo, executiveSummary) {
|
| 11 |
+
return new Promise((resolve, reject) => {
|
| 12 |
+
try {
|
| 13 |
+
const reportsDir = path.join(__dirname, '../reports');
|
| 14 |
+
if (!fs.existsSync(reportsDir)) {
|
| 15 |
+
fs.mkdirSync(reportsDir, { recursive: true });
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const filename = `NDMA_Training_Report_${Date.now()}.pdf`;
|
| 19 |
+
const filepath = path.join(reportsDir, filename);
|
| 20 |
+
|
| 21 |
+
const doc = new PDFDocument({
|
| 22 |
+
size: 'A4',
|
| 23 |
+
margins: { top: 60, bottom: 60, left: 60, right: 60 }
|
| 24 |
+
});
|
| 25 |
+
|
| 26 |
+
const stream = fs.createWriteStream(filepath);
|
| 27 |
+
doc.pipe(stream);
|
| 28 |
+
|
| 29 |
+
// Generate report
|
| 30 |
+
this.addCoverPage(doc, documentInfo);
|
| 31 |
+
this.addExecutiveSummary(doc, executiveSummary);
|
| 32 |
+
this.addKeyMetrics(doc, analysisResults);
|
| 33 |
+
this.addDetailedAnalysis(doc, analysisResults);
|
| 34 |
+
this.addGapAnalysis(doc, analysisResults);
|
| 35 |
+
this.addInsights(doc, analysisResults);
|
| 36 |
+
this.addRecommendations(doc, analysisResults);
|
| 37 |
+
|
| 38 |
+
doc.end();
|
| 39 |
+
|
| 40 |
+
stream.on('finish', () => {
|
| 41 |
+
resolve({ filename, filepath, url: `/reports/${filename}` });
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
stream.on('error', (error) => {
|
| 45 |
+
reject(error);
|
| 46 |
+
});
|
| 47 |
+
} catch (error) {
|
| 48 |
+
reject(error);
|
| 49 |
+
}
|
| 50 |
+
});
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
addCoverPage(doc, documentInfo) {
|
| 54 |
+
// Top Border
|
| 55 |
+
doc.rect(0, 0, doc.page.width, 10).fill('#1e40af');
|
| 56 |
+
|
| 57 |
+
// Government Header
|
| 58 |
+
doc.fontSize(28).font('Helvetica-Bold').fillColor('#1e40af')
|
| 59 |
+
.text('GOVERNMENT OF INDIA', 0, 100, { align: 'center' }).moveDown(0.3);
|
| 60 |
+
|
| 61 |
+
doc.fontSize(24).fillColor('#2563eb')
|
| 62 |
+
.text('National Disaster Management Authority', { align: 'center' }).moveDown(0.2);
|
| 63 |
+
|
| 64 |
+
doc.fontSize(12).font('Helvetica').fillColor('#6b7280')
|
| 65 |
+
.text('Ministry of Home Affairs', { align: 'center' }).moveDown(3);
|
| 66 |
+
|
| 67 |
+
// Title Box
|
| 68 |
+
const titleY = doc.y;
|
| 69 |
+
doc.roundedRect(80, titleY, 440, 100, 10).fillAndStroke('#dbeafe', '#3b82f6');
|
| 70 |
+
|
| 71 |
+
doc.fontSize(22).font('Helvetica-Bold').fillColor('#1e40af')
|
| 72 |
+
.text('DISASTER MANAGEMENT', 80, titleY + 15, { width: 440, align: 'center' }).moveDown(0.3);
|
| 73 |
+
|
| 74 |
+
doc.fontSize(20).fillColor('#2563eb')
|
| 75 |
+
.text('TRAINING ANALYSIS REPORT', 80, doc.y, { width: 440, align: 'center' });
|
| 76 |
+
|
| 77 |
+
doc.moveDown(4);
|
| 78 |
+
|
| 79 |
+
// Report Details
|
| 80 |
+
const currentDate = new Date().toLocaleDateString('en-IN', {
|
| 81 |
+
day: '2-digit', month: 'long', year: 'numeric'
|
| 82 |
+
});
|
| 83 |
+
|
| 84 |
+
const detailsY = doc.y;
|
| 85 |
+
doc.roundedRect(120, detailsY, 360, 100, 8).fillAndStroke('#f8fafc', '#cbd5e1');
|
| 86 |
+
|
| 87 |
+
doc.fontSize(11).font('Helvetica-Bold').fillColor('#374151')
|
| 88 |
+
.text('📅 Report Date:', 140, detailsY + 15);
|
| 89 |
+
doc.font('Helvetica').text(currentDate, 250, detailsY + 15);
|
| 90 |
+
|
| 91 |
+
doc.font('Helvetica-Bold').text('📄 Document:', 140, detailsY + 35);
|
| 92 |
+
doc.font('Helvetica').text(documentInfo.originalName || 'Training Data', 250, detailsY + 35, { width: 210 });
|
| 93 |
+
|
| 94 |
+
doc.font('Helvetica-Bold').text('🔍 Analysis Type:', 140, detailsY + 55);
|
| 95 |
+
doc.font('Helvetica').text('Comprehensive Analysis', 250, detailsY + 55);
|
| 96 |
+
|
| 97 |
+
doc.font('Helvetica-Bold').text('✅ Status:', 140, detailsY + 75);
|
| 98 |
+
doc.font('Helvetica').fillColor('#059669').text('COMPLETED', 250, detailsY + 75);
|
| 99 |
+
|
| 100 |
+
doc.moveDown(4);
|
| 101 |
+
|
| 102 |
+
// Confidentiality Notice
|
| 103 |
+
const noticeY = doc.y;
|
| 104 |
+
doc.roundedRect(100, noticeY, 400, 50, 5).fillAndStroke('#fee2e2', '#dc2626');
|
| 105 |
+
|
| 106 |
+
doc.fontSize(11).font('Helvetica-Bold').fillColor('#991b1b')
|
| 107 |
+
.text('⚠️ CONFIDENTIAL DOCUMENT', 0, noticeY + 10, { align: 'center' }).moveDown(0.3);
|
| 108 |
+
|
| 109 |
+
doc.fontSize(9).font('Helvetica').fillColor('#7f1d1d')
|
| 110 |
+
.text('For official use only. Unauthorized distribution prohibited.', 0, doc.y, { align: 'center' });
|
| 111 |
+
|
| 112 |
+
// Bottom Border
|
| 113 |
+
doc.rect(0, doc.page.height - 10, doc.page.width, 10).fill('#1e40af');
|
| 114 |
+
|
| 115 |
+
doc.addPage();
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
addExecutiveSummary(doc, summary) {
|
| 119 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#1e40af')
|
| 120 |
+
.text('📋 EXECUTIVE SUMMARY', { underline: true }).moveDown(1);
|
| 121 |
+
|
| 122 |
+
const summaryY = doc.y;
|
| 123 |
+
const summaryHeight = doc.heightOfString(summary, { width: 460, align: 'justify' });
|
| 124 |
+
|
| 125 |
+
doc.roundedRect(60, summaryY - 10, 480, summaryHeight + 30, 8)
|
| 126 |
+
.fillAndStroke('#eff6ff', '#93c5fd');
|
| 127 |
+
|
| 128 |
+
doc.fontSize(11).font('Helvetica').fillColor('#1f2937')
|
| 129 |
+
.text(summary, 70, summaryY, { width: 460, align: 'justify', lineGap: 5 });
|
| 130 |
+
|
| 131 |
+
doc.addPage();
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
addKeyMetrics(doc, analysis) {
|
| 135 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#1e40af')
|
| 136 |
+
.text('📊 KEY PERFORMANCE METRICS', { underline: true }).moveDown(1.5);
|
| 137 |
+
|
| 138 |
+
const metrics = [
|
| 139 |
+
{ icon: '🎯', label: 'Total Training Sessions', value: analysis.totalTrainings || 0, color: '#3b82f6', bg: '#dbeafe' },
|
| 140 |
+
{ icon: '👥', label: 'Total Participants', value: analysis.totalParticipants?.toLocaleString() || 0, color: '#10b981', bg: '#d1fae5' },
|
| 141 |
+
{ icon: '📈', label: 'Completion Rate', value: analysis.averageCompletionRate || 'N/A', color: '#f59e0b', bg: '#fef3c7' },
|
| 142 |
+
{ icon: '📍', label: 'States Covered', value: Object.keys(analysis.stateWiseCoverage || {}).length, color: '#8b5cf6', bg: '#ede9fe' },
|
| 143 |
+
{ icon: '🎓', label: 'Disaster Themes', value: Object.keys(analysis.themeDistribution || {}).length, color: '#ec4899', bg: '#fce7f3' }
|
| 144 |
+
];
|
| 145 |
+
|
| 146 |
+
let yPos = doc.y;
|
| 147 |
+
metrics.forEach((metric) => {
|
| 148 |
+
doc.roundedRect(60, yPos, 480, 50, 8).fillAndStroke(metric.bg, metric.color);
|
| 149 |
+
doc.fontSize(24).text(metric.icon, 75, yPos + 13);
|
| 150 |
+
doc.fontSize(12).font('Helvetica-Bold').fillColor('#374151')
|
| 151 |
+
.text(metric.label, 115, yPos + 12);
|
| 152 |
+
doc.fontSize(20).font('Helvetica-Bold').fillColor(metric.color)
|
| 153 |
+
.text(metric.value.toString(), 380, yPos + 10, { width: 150, align: 'right' });
|
| 154 |
+
yPos += 60;
|
| 155 |
+
});
|
| 156 |
+
|
| 157 |
+
doc.addPage();
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
addDetailedAnalysis(doc, analysis) {
|
| 161 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#1e40af')
|
| 162 |
+
.text('📈 DETAILED ANALYSIS', { underline: true }).moveDown(1.5);
|
| 163 |
+
|
| 164 |
+
// Theme Distribution
|
| 165 |
+
if (analysis.themeDistribution && Object.keys(analysis.themeDistribution).length > 0) {
|
| 166 |
+
doc.fontSize(14).font('Helvetica-Bold').fillColor('#2563eb')
|
| 167 |
+
.text('Theme Distribution', 60).moveDown(0.8);
|
| 168 |
+
|
| 169 |
+
const themes = Object.entries(analysis.themeDistribution).sort((a, b) => b[1] - a[1]);
|
| 170 |
+
themes.forEach(([theme, count], idx) => {
|
| 171 |
+
const percentage = ((count / analysis.totalTrainings) * 100).toFixed(1);
|
| 172 |
+
doc.fontSize(10).font('Helvetica').fillColor('#1f2937')
|
| 173 |
+
.text(`${idx + 1}. ${theme}: ${count} sessions (${percentage}%)`, { indent: 20 })
|
| 174 |
+
.moveDown(0.3);
|
| 175 |
+
});
|
| 176 |
+
doc.moveDown(1);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// State Coverage
|
| 180 |
+
if (analysis.stateWiseCoverage && Object.keys(analysis.stateWiseCoverage).length > 0) {
|
| 181 |
+
doc.fontSize(14).font('Helvetica-Bold').fillColor('#2563eb')
|
| 182 |
+
.text('Top 10 States', 60).moveDown(0.8);
|
| 183 |
+
|
| 184 |
+
const states = Object.entries(analysis.stateWiseCoverage)
|
| 185 |
+
.sort((a, b) => b[1] - a[1]).slice(0, 10);
|
| 186 |
+
|
| 187 |
+
states.forEach(([state, count], idx) => {
|
| 188 |
+
doc.fontSize(10).font('Helvetica').fillColor('#1f2937')
|
| 189 |
+
.text(`${idx + 1}. ${state}: ${count} sessions`, { indent: 20 })
|
| 190 |
+
.moveDown(0.3);
|
| 191 |
+
});
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
doc.addPage();
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
addGapAnalysis(doc, analysis) {
|
| 198 |
+
if (!analysis.gapAnalysis) return;
|
| 199 |
+
|
| 200 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#dc2626')
|
| 201 |
+
.text('⚠️ GAP ANALYSIS', { underline: true }).moveDown(1.5);
|
| 202 |
+
|
| 203 |
+
if (analysis.gapAnalysis.underservedStates?.length > 0) {
|
| 204 |
+
doc.fontSize(13).font('Helvetica-Bold').fillColor('#f59e0b')
|
| 205 |
+
.text('Underserved States', 60).moveDown(0.5);
|
| 206 |
+
|
| 207 |
+
analysis.gapAnalysis.underservedStates.forEach((state, idx) => {
|
| 208 |
+
doc.fontSize(10).font('Helvetica').fillColor('#92400e')
|
| 209 |
+
.text(`${idx + 1}. ${state}`, { indent: 20 }).moveDown(0.3);
|
| 210 |
+
});
|
| 211 |
+
doc.moveDown(1);
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
if (analysis.gapAnalysis.criticalGaps?.length > 0) {
|
| 215 |
+
doc.fontSize(13).font('Helvetica-Bold').fillColor('#dc2626')
|
| 216 |
+
.text('Critical Gaps', 60).moveDown(0.5);
|
| 217 |
+
|
| 218 |
+
analysis.gapAnalysis.criticalGaps.forEach((gap, idx) => {
|
| 219 |
+
doc.fontSize(10).font('Helvetica').fillColor('#7f1d1d')
|
| 220 |
+
.text(`${idx + 1}. ${gap}`, { indent: 20, align: 'justify' })
|
| 221 |
+
.moveDown(0.5);
|
| 222 |
+
});
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
doc.addPage();
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
addInsights(doc, analysis) {
|
| 229 |
+
if (!analysis.keyInsights?.length) return;
|
| 230 |
+
|
| 231 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#1e40af')
|
| 232 |
+
.text('💡 BUSINESS INSIGHTS', { underline: true }).moveDown(1.5);
|
| 233 |
+
|
| 234 |
+
analysis.keyInsights.forEach((insight, idx) => {
|
| 235 |
+
doc.fontSize(10).font('Helvetica').fillColor('#1e40af')
|
| 236 |
+
.text(`${idx + 1}. ${insight}`, { indent: 20, align: 'justify' })
|
| 237 |
+
.moveDown(0.8);
|
| 238 |
+
});
|
| 239 |
+
|
| 240 |
+
doc.addPage();
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
addRecommendations(doc, analysis) {
|
| 244 |
+
if (!analysis.recommendations?.length) return;
|
| 245 |
+
|
| 246 |
+
doc.fontSize(18).font('Helvetica-Bold').fillColor('#059669')
|
| 247 |
+
.text('✅ STRATEGIC RECOMMENDATIONS', { underline: true }).moveDown(1.5);
|
| 248 |
+
|
| 249 |
+
analysis.recommendations.forEach((rec, idx) => {
|
| 250 |
+
doc.fontSize(10).font('Helvetica-Bold').fillColor('#065f46')
|
| 251 |
+
.text(`${idx + 1}. ${rec}`, { indent: 20, align: 'justify' })
|
| 252 |
+
.moveDown(0.8);
|
| 253 |
+
});
|
| 254 |
+
|
| 255 |
+
// Footer
|
| 256 |
+
doc.moveDown(2);
|
| 257 |
+
doc.fontSize(9).font('Helvetica').fillColor('#6b7280')
|
| 258 |
+
.text('© National Disaster Management Authority, Government of India', { align: 'center' });
|
| 259 |
+
}
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
export default new ReportGenerator();
|
backend/test-direct-api.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fetch from 'node-fetch';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
|
| 4 |
+
dotenv.config();
|
| 5 |
+
|
| 6 |
+
async function testGeminiDirect() {
|
| 7 |
+
const apiKey = process.env.GEMINI_API_KEY || 'AIzaSyAQudSMm7BbOkL3P5f01FoefcboH9jBHQo';
|
| 8 |
+
|
| 9 |
+
// Try different API versions and models
|
| 10 |
+
const tests = [
|
| 11 |
+
{
|
| 12 |
+
url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`,
|
| 13 |
+
model: 'gemini-pro (v1beta)'
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
url: `https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${apiKey}`,
|
| 17 |
+
model: 'gemini-pro (v1)'
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`,
|
| 21 |
+
model: 'gemini-1.5-flash (v1beta)'
|
| 22 |
+
}
|
| 23 |
+
];
|
| 24 |
+
|
| 25 |
+
console.log('🔍 Testing Gemini API directly...\n');
|
| 26 |
+
|
| 27 |
+
for (const test of tests) {
|
| 28 |
+
try {
|
| 29 |
+
console.log(`Testing: ${test.model}`);
|
| 30 |
+
|
| 31 |
+
const response = await fetch(test.url, {
|
| 32 |
+
method: 'POST',
|
| 33 |
+
headers: { 'Content-Type': 'application/json' },
|
| 34 |
+
body: JSON.stringify({
|
| 35 |
+
contents: [{
|
| 36 |
+
parts: [{ text: 'Say hello' }]
|
| 37 |
+
}]
|
| 38 |
+
})
|
| 39 |
+
});
|
| 40 |
+
|
| 41 |
+
const data = await response.json();
|
| 42 |
+
|
| 43 |
+
if (response.ok) {
|
| 44 |
+
console.log(`✅ SUCCESS: ${test.model} works!`);
|
| 45 |
+
console.log(` Response: ${JSON.stringify(data, null, 2)}\n`);
|
| 46 |
+
return test;
|
| 47 |
+
} else {
|
| 48 |
+
console.log(`❌ FAILED: ${test.model}`);
|
| 49 |
+
console.log(` Status: ${response.status}`);
|
| 50 |
+
console.log(` Error: ${data.error?.message || JSON.stringify(data)}\n`);
|
| 51 |
+
}
|
| 52 |
+
} catch (error) {
|
| 53 |
+
console.log(`❌ ERROR: ${test.model}`);
|
| 54 |
+
console.log(` ${error.message}\n`);
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
console.log('❌ All tests failed! API key might be invalid.');
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
testGeminiDirect();
|
backend/test-gemini.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
| 2 |
+
import dotenv from 'dotenv';
|
| 3 |
+
|
| 4 |
+
dotenv.config();
|
| 5 |
+
|
| 6 |
+
async function testGeminiModels() {
|
| 7 |
+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
|
| 8 |
+
|
| 9 |
+
const modelsToTry = [
|
| 10 |
+
'gemini-pro',
|
| 11 |
+
'gemini-1.5-pro',
|
| 12 |
+
'gemini-1.5-flash',
|
| 13 |
+
'gemini-1.5-flash-latest',
|
| 14 |
+
'models/gemini-pro',
|
| 15 |
+
'models/gemini-1.5-pro',
|
| 16 |
+
'models/gemini-1.5-flash'
|
| 17 |
+
];
|
| 18 |
+
|
| 19 |
+
console.log('🔍 Testing Gemini models with your API key...\n');
|
| 20 |
+
|
| 21 |
+
for (const modelName of modelsToTry) {
|
| 22 |
+
try {
|
| 23 |
+
console.log(`Testing: ${modelName}`);
|
| 24 |
+
const model = genAI.getGenerativeModel({ model: modelName });
|
| 25 |
+
const result = await model.generateContent('Say "Hello"');
|
| 26 |
+
const response = await result.response;
|
| 27 |
+
const text = response.text();
|
| 28 |
+
console.log(`✅ SUCCESS: ${modelName} works!`);
|
| 29 |
+
console.log(` Response: ${text}\n`);
|
| 30 |
+
break; // Stop after first success
|
| 31 |
+
} catch (error) {
|
| 32 |
+
console.log(`❌ FAILED: ${modelName}`);
|
| 33 |
+
console.log(` Error: ${error.message}\n`);
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
testGeminiModels();
|
frontend/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<meta name="description" content="RAG Document Analysis System for Disaster Management Training Data - NDMA" />
|
| 8 |
+
<title>RAG Document Analysis | NDMA</title>
|
| 9 |
+
</head>
|
| 10 |
+
<body>
|
| 11 |
+
<div id="root"></div>
|
| 12 |
+
<script type="module" src="/src/main.jsx"></script>
|
| 13 |
+
</body>
|
| 14 |
+
</html>
|
frontend/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "rag-document-analysis-frontend",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "Frontend for RAG-based document analysis system",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "vite build",
|
| 9 |
+
"preview": "vite preview"
|
| 10 |
+
},
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"react": "^18.2.0",
|
| 13 |
+
"react-dom": "^18.2.0",
|
| 14 |
+
"react-router-dom": "^6.20.1",
|
| 15 |
+
"axios": "^1.6.2",
|
| 16 |
+
"react-dropzone": "^14.2.3",
|
| 17 |
+
"recharts": "^2.10.3",
|
| 18 |
+
"lucide-react": "^0.294.0"
|
| 19 |
+
},
|
| 20 |
+
"devDependencies": {
|
| 21 |
+
"@types/react": "^18.2.43",
|
| 22 |
+
"@types/react-dom": "^18.2.17",
|
| 23 |
+
"@vitejs/plugin-react": "^4.2.1",
|
| 24 |
+
"vite": "^5.0.8"
|
| 25 |
+
}
|
| 26 |
+
}
|
frontend/src/App.jsx
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react';
|
| 2 |
+
import FileUpload from './components/FileUpload';
|
| 3 |
+
import AnalysisDashboard from './components/AnalysisDashboard';
|
| 4 |
+
import ReportPreview from './components/ReportPreview';
|
| 5 |
+
import { FileText, BarChart3, Download } from 'lucide-react';
|
| 6 |
+
|
| 7 |
+
function App() {
|
| 8 |
+
const [currentView, setCurrentView] = useState('upload');
|
| 9 |
+
const [analysisData, setAnalysisData] = useState(null);
|
| 10 |
+
const [reportData, setReportData] = useState(null);
|
| 11 |
+
|
| 12 |
+
const handleAnalysisComplete = (data) => {
|
| 13 |
+
setAnalysisData(data);
|
| 14 |
+
setReportData(data);
|
| 15 |
+
setCurrentView('dashboard');
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
return (
|
| 19 |
+
<div style={{ minHeight: '100vh', paddingBottom: '2rem' }}>
|
| 20 |
+
{/* Header */}
|
| 21 |
+
<header style={{
|
| 22 |
+
background: 'rgba(255, 255, 255, 0.98)',
|
| 23 |
+
backdropFilter: 'blur(30px) saturate(180%)',
|
| 24 |
+
borderBottom: '1px solid rgba(102, 126, 234, 0.2)',
|
| 25 |
+
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.08)',
|
| 26 |
+
position: 'sticky',
|
| 27 |
+
top: 0,
|
| 28 |
+
zIndex: 100
|
| 29 |
+
}}>
|
| 30 |
+
<div className="container" style={{ padding: '1.5rem' }}>
|
| 31 |
+
<div className="flex items-center justify-between">
|
| 32 |
+
<div className="flex items-center gap-3">
|
| 33 |
+
<div style={{
|
| 34 |
+
width: '56px',
|
| 35 |
+
height: '56px',
|
| 36 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 37 |
+
borderRadius: '16px',
|
| 38 |
+
display: 'flex',
|
| 39 |
+
alignItems: 'center',
|
| 40 |
+
justifyContent: 'center',
|
| 41 |
+
color: 'white',
|
| 42 |
+
fontSize: '1.75rem',
|
| 43 |
+
fontWeight: 'bold',
|
| 44 |
+
boxShadow: '0 8px 24px rgba(102, 126, 234, 0.4)',
|
| 45 |
+
transition: 'transform 0.3s ease'
|
| 46 |
+
}}
|
| 47 |
+
onMouseEnter={(e) => e.currentTarget.style.transform = 'rotate(10deg) scale(1.1)'}
|
| 48 |
+
onMouseLeave={(e) => e.currentTarget.style.transform = 'rotate(0deg) scale(1)'}
|
| 49 |
+
>
|
| 50 |
+
📊
|
| 51 |
+
</div>
|
| 52 |
+
<div>
|
| 53 |
+
<h1 style={{
|
| 54 |
+
fontSize: '1.75rem',
|
| 55 |
+
fontWeight: '800',
|
| 56 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 57 |
+
WebkitBackgroundClip: 'text',
|
| 58 |
+
WebkitTextFillColor: 'transparent',
|
| 59 |
+
marginBottom: '0.25rem',
|
| 60 |
+
letterSpacing: '-0.5px'
|
| 61 |
+
}}>
|
| 62 |
+
RAG Document Analysis
|
| 63 |
+
</h1>
|
| 64 |
+
<p style={{
|
| 65 |
+
fontSize: '0.9rem',
|
| 66 |
+
color: '#6b7280',
|
| 67 |
+
fontWeight: '500'
|
| 68 |
+
}}>
|
| 69 |
+
National Disaster Management Authority
|
| 70 |
+
</p>
|
| 71 |
+
</div>
|
| 72 |
+
</div>
|
| 73 |
+
|
| 74 |
+
<div className="flex gap-2">
|
| 75 |
+
<button
|
| 76 |
+
onClick={() => setCurrentView('upload')}
|
| 77 |
+
className={`btn ${currentView === 'upload' ? 'btn-primary' : 'btn-secondary'}`}
|
| 78 |
+
>
|
| 79 |
+
<FileText size={18} />
|
| 80 |
+
Upload
|
| 81 |
+
</button>
|
| 82 |
+
<button
|
| 83 |
+
onClick={() => setCurrentView('dashboard')}
|
| 84 |
+
className={`btn ${currentView === 'dashboard' ? 'btn-primary' : 'btn-secondary'}`}
|
| 85 |
+
disabled={!analysisData}
|
| 86 |
+
>
|
| 87 |
+
<BarChart3 size={18} />
|
| 88 |
+
Dashboard
|
| 89 |
+
</button>
|
| 90 |
+
<button
|
| 91 |
+
onClick={() => setCurrentView('report')}
|
| 92 |
+
className={`btn ${currentView === 'report' ? 'btn-primary' : 'btn-secondary'}`}
|
| 93 |
+
disabled={!reportData}
|
| 94 |
+
>
|
| 95 |
+
<Download size={18} />
|
| 96 |
+
Report
|
| 97 |
+
</button>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
</div>
|
| 101 |
+
</header>
|
| 102 |
+
|
| 103 |
+
{/* Main Content */}
|
| 104 |
+
<main className="container" style={{ marginTop: '2rem' }}>
|
| 105 |
+
{currentView === 'upload' && (
|
| 106 |
+
<FileUpload onAnalysisComplete={handleAnalysisComplete} />
|
| 107 |
+
)}
|
| 108 |
+
|
| 109 |
+
{currentView === 'dashboard' && analysisData && (
|
| 110 |
+
<AnalysisDashboard data={analysisData} />
|
| 111 |
+
)}
|
| 112 |
+
|
| 113 |
+
{currentView === 'report' && reportData && (
|
| 114 |
+
<ReportPreview data={reportData} />
|
| 115 |
+
)}
|
| 116 |
+
</main>
|
| 117 |
+
|
| 118 |
+
{/* Footer */}
|
| 119 |
+
<footer style={{
|
| 120 |
+
marginTop: '4rem',
|
| 121 |
+
padding: '2.5rem 0',
|
| 122 |
+
textAlign: 'center',
|
| 123 |
+
color: 'rgba(255, 255, 255, 0.95)',
|
| 124 |
+
fontSize: '0.9rem',
|
| 125 |
+
background: 'rgba(255, 255, 255, 0.1)',
|
| 126 |
+
backdropFilter: 'blur(20px)',
|
| 127 |
+
borderTop: '1px solid rgba(255, 255, 255, 0.2)',
|
| 128 |
+
fontWeight: '500'
|
| 129 |
+
}}>
|
| 130 |
+
<p>© 2025 National Disaster Management Authority, Government of India</p>
|
| 131 |
+
<p style={{ marginTop: '0.625rem', fontSize: '0.85rem', opacity: 0.9 }}>
|
| 132 |
+
Developed by JARVIS GGV | Smart India Hackathon 2025
|
| 133 |
+
</p>
|
| 134 |
+
</footer>
|
| 135 |
+
</div>
|
| 136 |
+
);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
export default App;
|
frontend/src/components/AnalysisDashboard.jsx
ADDED
|
@@ -0,0 +1,841 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from 'react';
|
| 2 |
+
import { BarChart, Bar, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
| 3 |
+
import { TrendingUp, Users, Award, MapPin, AlertTriangle, Lightbulb, Target, X, HelpCircle, Book } from 'lucide-react';
|
| 4 |
+
|
| 5 |
+
function AnalysisDashboard({ data }) {
|
| 6 |
+
const { analysis, executiveSummary } = data;
|
| 7 |
+
const [selectedTheme, setSelectedTheme] = useState(null);
|
| 8 |
+
const [showModal, setShowModal] = useState(false);
|
| 9 |
+
const [showDocs, setShowDocs] = useState(false);
|
| 10 |
+
|
| 11 |
+
// Prepare chart data
|
| 12 |
+
const themeData = Object.entries(analysis.themeDistribution || {}).map(([name, value]) => ({
|
| 13 |
+
name,
|
| 14 |
+
value
|
| 15 |
+
}));
|
| 16 |
+
|
| 17 |
+
const stateData = Object.entries(analysis.stateWiseCoverage || {})
|
| 18 |
+
.map(([name, value]) => ({ name, value }))
|
| 19 |
+
.sort((a, b) => b.value - a.value)
|
| 20 |
+
.slice(0, 8);
|
| 21 |
+
|
| 22 |
+
const COLORS = ['#667eea', '#764ba2', '#f093fb', '#4facfe', '#43e97b', '#fa709a', '#fee140', '#30cfd0'];
|
| 23 |
+
|
| 24 |
+
// Get states for selected theme from analysis data
|
| 25 |
+
const getThemeDetails = (themeName) => {
|
| 26 |
+
const themeStates = analysis.themeStateMapping?.[themeName] || {};
|
| 27 |
+
const statesWithCounts = Object.entries(themeStates)
|
| 28 |
+
.map(([state, count]) => ({ state, count }))
|
| 29 |
+
.sort((a, b) => b.count - a.count);
|
| 30 |
+
|
| 31 |
+
return {
|
| 32 |
+
theme: themeName,
|
| 33 |
+
count: analysis.themeDistribution[themeName],
|
| 34 |
+
states: statesWithCounts
|
| 35 |
+
};
|
| 36 |
+
};
|
| 37 |
+
|
| 38 |
+
const handlePieClick = (data) => {
|
| 39 |
+
const themeDetails = getThemeDetails(data.name);
|
| 40 |
+
setSelectedTheme(themeDetails);
|
| 41 |
+
setShowModal(true);
|
| 42 |
+
};
|
| 43 |
+
|
| 44 |
+
return (
|
| 45 |
+
<div className="fade-in" style={{ paddingBottom: '2rem', position: 'relative' }}>
|
| 46 |
+
{/* Floating Help Button */}
|
| 47 |
+
<button
|
| 48 |
+
onClick={() => setShowDocs(true)}
|
| 49 |
+
style={{
|
| 50 |
+
position: 'fixed',
|
| 51 |
+
bottom: '2rem',
|
| 52 |
+
right: '2rem',
|
| 53 |
+
width: '60px',
|
| 54 |
+
height: '60px',
|
| 55 |
+
borderRadius: '50%',
|
| 56 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 57 |
+
border: 'none',
|
| 58 |
+
boxShadow: '0 8px 24px rgba(102, 126, 234, 0.4)',
|
| 59 |
+
cursor: 'pointer',
|
| 60 |
+
display: 'flex',
|
| 61 |
+
alignItems: 'center',
|
| 62 |
+
justifyContent: 'center',
|
| 63 |
+
zIndex: 999,
|
| 64 |
+
transition: 'all 0.3s ease'
|
| 65 |
+
}}
|
| 66 |
+
onMouseEnter={(e) => {
|
| 67 |
+
e.currentTarget.style.transform = 'scale(1.1)';
|
| 68 |
+
e.currentTarget.style.boxShadow = '0 12px 32px rgba(102, 126, 234, 0.5)';
|
| 69 |
+
}}
|
| 70 |
+
onMouseLeave={(e) => {
|
| 71 |
+
e.currentTarget.style.transform = 'scale(1)';
|
| 72 |
+
e.currentTarget.style.boxShadow = '0 8px 24px rgba(102, 126, 234, 0.4)';
|
| 73 |
+
}}
|
| 74 |
+
title="View Documentation"
|
| 75 |
+
>
|
| 76 |
+
<Book size={28} color="white" />
|
| 77 |
+
</button>
|
| 78 |
+
|
| 79 |
+
{/* Documentation Modal */}
|
| 80 |
+
{showDocs && (
|
| 81 |
+
<div style={{
|
| 82 |
+
position: 'fixed',
|
| 83 |
+
top: 0,
|
| 84 |
+
left: 0,
|
| 85 |
+
right: 0,
|
| 86 |
+
bottom: 0,
|
| 87 |
+
background: 'rgba(0, 0, 0, 0.5)',
|
| 88 |
+
display: 'flex',
|
| 89 |
+
alignItems: 'center',
|
| 90 |
+
justifyContent: 'center',
|
| 91 |
+
zIndex: 1000,
|
| 92 |
+
backdropFilter: 'blur(4px)',
|
| 93 |
+
padding: '2rem'
|
| 94 |
+
}}
|
| 95 |
+
onClick={() => setShowDocs(false)}
|
| 96 |
+
>
|
| 97 |
+
<div style={{
|
| 98 |
+
background: 'white',
|
| 99 |
+
borderRadius: '16px',
|
| 100 |
+
padding: '2rem',
|
| 101 |
+
maxWidth: '900px',
|
| 102 |
+
width: '100%',
|
| 103 |
+
maxHeight: '90vh',
|
| 104 |
+
overflow: 'auto',
|
| 105 |
+
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
|
| 106 |
+
position: 'relative'
|
| 107 |
+
}}
|
| 108 |
+
onClick={(e) => e.stopPropagation()}
|
| 109 |
+
>
|
| 110 |
+
{/* Close Button */}
|
| 111 |
+
<button
|
| 112 |
+
onClick={() => setShowDocs(false)}
|
| 113 |
+
style={{
|
| 114 |
+
position: 'absolute',
|
| 115 |
+
top: '1rem',
|
| 116 |
+
right: '1rem',
|
| 117 |
+
background: 'rgba(239, 68, 68, 0.1)',
|
| 118 |
+
border: 'none',
|
| 119 |
+
borderRadius: '50%',
|
| 120 |
+
width: '36px',
|
| 121 |
+
height: '36px',
|
| 122 |
+
display: 'flex',
|
| 123 |
+
alignItems: 'center',
|
| 124 |
+
justifyContent: 'center',
|
| 125 |
+
cursor: 'pointer',
|
| 126 |
+
transition: 'all 0.3s ease'
|
| 127 |
+
}}
|
| 128 |
+
onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(239, 68, 68, 0.2)'}
|
| 129 |
+
onMouseLeave={(e) => e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)'}
|
| 130 |
+
>
|
| 131 |
+
<X size={20} color="#ef4444" />
|
| 132 |
+
</button>
|
| 133 |
+
|
| 134 |
+
{/* Documentation Content */}
|
| 135 |
+
<div style={{ paddingRight: '2rem' }}>
|
| 136 |
+
<h1 style={{
|
| 137 |
+
fontSize: '2rem',
|
| 138 |
+
fontWeight: '800',
|
| 139 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 140 |
+
WebkitBackgroundClip: 'text',
|
| 141 |
+
WebkitTextFillColor: 'transparent',
|
| 142 |
+
marginBottom: '0.5rem'
|
| 143 |
+
}}>
|
| 144 |
+
📚 System Documentation
|
| 145 |
+
</h1>
|
| 146 |
+
<p style={{ color: '#6b7280', marginBottom: '2rem' }}>
|
| 147 |
+
Complete guide on how the RAG Document Analysis System works
|
| 148 |
+
</p>
|
| 149 |
+
|
| 150 |
+
{/* Quick Links */}
|
| 151 |
+
<div style={{
|
| 152 |
+
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%)',
|
| 153 |
+
padding: '1.5rem',
|
| 154 |
+
borderRadius: '12px',
|
| 155 |
+
marginBottom: '2rem',
|
| 156 |
+
border: '1px solid rgba(102, 126, 234, 0.2)'
|
| 157 |
+
}}>
|
| 158 |
+
<h3 style={{ fontSize: '1.1rem', fontWeight: '700', marginBottom: '1rem', color: '#667eea' }}>
|
| 159 |
+
🚀 Quick Start
|
| 160 |
+
</h3>
|
| 161 |
+
<ol style={{ marginLeft: '1.5rem', lineHeight: '2' }}>
|
| 162 |
+
<li><strong>Upload CSV/Excel file</strong> with training data</li>
|
| 163 |
+
<li><strong>Wait for analysis</strong> (automatic, 100% FREE)</li>
|
| 164 |
+
<li><strong>View dashboard</strong> with insights and charts</li>
|
| 165 |
+
<li><strong>Click pie chart</strong> to see theme details</li>
|
| 166 |
+
<li><strong>Download PDF report</strong> for official use</li>
|
| 167 |
+
</ol>
|
| 168 |
+
</div>
|
| 169 |
+
|
| 170 |
+
{/* System Overview */}
|
| 171 |
+
<section style={{ marginBottom: '2rem' }}>
|
| 172 |
+
<h2 style={{ fontSize: '1.5rem', fontWeight: '700', color: '#1f2937', marginBottom: '1rem' }}>
|
| 173 |
+
📋 System Overview
|
| 174 |
+
</h2>
|
| 175 |
+
<p style={{ lineHeight: '1.8', color: '#374151' }}>
|
| 176 |
+
This is an <strong>AI-powered Training Data Analysis System</strong> designed for disaster management training programs.
|
| 177 |
+
It analyzes CSV/Excel files containing training records and generates comprehensive insights, gap analysis,
|
| 178 |
+
and professional PDF reports.
|
| 179 |
+
</p>
|
| 180 |
+
</section>
|
| 181 |
+
|
| 182 |
+
{/* Key Features */}
|
| 183 |
+
<section style={{ marginBottom: '2rem' }}>
|
| 184 |
+
<h2 style={{ fontSize: '1.5rem', fontWeight: '700', color: '#1f2937', marginBottom: '1rem' }}>
|
| 185 |
+
✨ Key Features
|
| 186 |
+
</h2>
|
| 187 |
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
| 188 |
+
{[
|
| 189 |
+
{ icon: '📊', title: 'Interactive Charts', desc: 'Click pie chart to see theme-wise state details' },
|
| 190 |
+
{ icon: '📈', title: 'Real-time Analysis', desc: 'Instant insights from your training data' },
|
| 191 |
+
{ icon: '📄', title: 'PDF Reports', desc: 'Government-format professional reports' },
|
| 192 |
+
{ icon: '🆓', title: '100% FREE', desc: 'No API keys or subscriptions needed' },
|
| 193 |
+
{ icon: '⚡', title: 'Fast Processing', desc: 'Analyze thousands of records in seconds' },
|
| 194 |
+
{ icon: '🎯', title: 'Gap Analysis', desc: 'Identify underserved states and themes' }
|
| 195 |
+
].map((feature, idx) => (
|
| 196 |
+
<div key={idx} style={{
|
| 197 |
+
padding: '1rem',
|
| 198 |
+
background: 'rgba(102, 126, 234, 0.03)',
|
| 199 |
+
borderRadius: '8px',
|
| 200 |
+
border: '1px solid rgba(102, 126, 234, 0.1)'
|
| 201 |
+
}}>
|
| 202 |
+
<div style={{ fontSize: '1.5rem', marginBottom: '0.5rem' }}>{feature.icon}</div>
|
| 203 |
+
<h4 style={{ fontSize: '0.95rem', fontWeight: '600', marginBottom: '0.25rem', color: '#1f2937' }}>
|
| 204 |
+
{feature.title}
|
| 205 |
+
</h4>
|
| 206 |
+
<p style={{ fontSize: '0.85rem', color: '#6b7280', lineHeight: '1.5' }}>
|
| 207 |
+
{feature.desc}
|
| 208 |
+
</p>
|
| 209 |
+
</div>
|
| 210 |
+
))}
|
| 211 |
+
</div>
|
| 212 |
+
</section>
|
| 213 |
+
|
| 214 |
+
{/* How It Works */}
|
| 215 |
+
<section style={{ marginBottom: '2rem' }}>
|
| 216 |
+
<h2 style={{ fontSize: '1.5rem', fontWeight: '700', color: '#1f2937', marginBottom: '1rem' }}>
|
| 217 |
+
🔄 How It Works
|
| 218 |
+
</h2>
|
| 219 |
+
<div style={{ position: 'relative', paddingLeft: '2rem' }}>
|
| 220 |
+
{[
|
| 221 |
+
{ step: '1', title: 'File Upload', desc: 'Drag & drop or select CSV/Excel file' },
|
| 222 |
+
{ step: '2', title: 'Data Parsing', desc: 'System extracts training records from file' },
|
| 223 |
+
{ step: '3', title: 'AI Analysis', desc: 'Local analysis calculates metrics and insights' },
|
| 224 |
+
{ step: '4', title: 'Dashboard Display', desc: 'Interactive charts and insights shown' },
|
| 225 |
+
{ step: '5', title: 'PDF Generation', desc: 'Professional report created automatically' }
|
| 226 |
+
].map((item, idx) => (
|
| 227 |
+
<div key={idx} style={{ marginBottom: '1.5rem', position: 'relative' }}>
|
| 228 |
+
<div style={{
|
| 229 |
+
position: 'absolute',
|
| 230 |
+
left: '-2rem',
|
| 231 |
+
top: '0',
|
| 232 |
+
width: '32px',
|
| 233 |
+
height: '32px',
|
| 234 |
+
borderRadius: '50%',
|
| 235 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 236 |
+
color: 'white',
|
| 237 |
+
display: 'flex',
|
| 238 |
+
alignItems: 'center',
|
| 239 |
+
justifyContent: 'center',
|
| 240 |
+
fontWeight: '700',
|
| 241 |
+
fontSize: '0.9rem'
|
| 242 |
+
}}>
|
| 243 |
+
{item.step}
|
| 244 |
+
</div>
|
| 245 |
+
<h4 style={{ fontSize: '1rem', fontWeight: '600', marginBottom: '0.25rem', color: '#1f2937' }}>
|
| 246 |
+
{item.title}
|
| 247 |
+
</h4>
|
| 248 |
+
<p style={{ fontSize: '0.9rem', color: '#6b7280', lineHeight: '1.6' }}>
|
| 249 |
+
{item.desc}
|
| 250 |
+
</p>
|
| 251 |
+
</div>
|
| 252 |
+
))}
|
| 253 |
+
</div>
|
| 254 |
+
</section>
|
| 255 |
+
|
| 256 |
+
{/* Dashboard Guide */}
|
| 257 |
+
<section style={{ marginBottom: '2rem' }}>
|
| 258 |
+
<h2 style={{ fontSize: '1.5rem', fontWeight: '700', color: '#1f2937', marginBottom: '1rem' }}>
|
| 259 |
+
🎯 Dashboard Guide
|
| 260 |
+
</h2>
|
| 261 |
+
<div style={{
|
| 262 |
+
background: '#f8fafc',
|
| 263 |
+
padding: '1.5rem',
|
| 264 |
+
borderRadius: '12px',
|
| 265 |
+
border: '1px solid #e2e8f0'
|
| 266 |
+
}}>
|
| 267 |
+
<h4 style={{ fontSize: '1rem', fontWeight: '600', marginBottom: '1rem', color: '#667eea' }}>
|
| 268 |
+
📊 Interactive Pie Chart
|
| 269 |
+
</h4>
|
| 270 |
+
<ul style={{ marginLeft: '1.5rem', lineHeight: '2', color: '#374151' }}>
|
| 271 |
+
<li><strong>Click any theme</strong> to see detailed breakdown</li>
|
| 272 |
+
<li>Popup shows <strong>states covered</strong> for that disaster type</li>
|
| 273 |
+
<li>Each state displays <strong>training count</strong> badge</li>
|
| 274 |
+
<li>Data is <strong>theme-specific</strong> (Flood shows only flood-affected states)</li>
|
| 275 |
+
</ul>
|
| 276 |
+
|
| 277 |
+
<h4 style={{ fontSize: '1rem', fontWeight: '600', margin: '1.5rem 0 1rem', color: '#43e97b' }}>
|
| 278 |
+
📍 State Coverage Chart
|
| 279 |
+
</h4>
|
| 280 |
+
<ul style={{ marginLeft: '1.5rem', lineHeight: '2', color: '#374151' }}>
|
| 281 |
+
<li>Shows <strong>top 8 states</strong> by training count</li>
|
| 282 |
+
<li>Sorted from highest to lowest</li>
|
| 283 |
+
<li>Hover to see exact numbers</li>
|
| 284 |
+
</ul>
|
| 285 |
+
|
| 286 |
+
<h4 style={{ fontSize: '1rem', fontWeight: '600', margin: '1.5rem 0 1rem', color: '#f093fb' }}>
|
| 287 |
+
⚠️ Gap Analysis
|
| 288 |
+
</h4>
|
| 289 |
+
<ul style={{ marginLeft: '1.5rem', lineHeight: '2', color: '#374151' }}>
|
| 290 |
+
<li><strong>Underserved States:</strong> States with less than 5 trainings</li>
|
| 291 |
+
<li><strong>Underserved Themes:</strong> Disaster types needing more coverage</li>
|
| 292 |
+
<li><strong>Critical Gaps:</strong> Important areas requiring immediate attention</li>
|
| 293 |
+
</ul>
|
| 294 |
+
</div>
|
| 295 |
+
</section>
|
| 296 |
+
|
| 297 |
+
{/* Technical Details */}
|
| 298 |
+
<section style={{ marginBottom: '2rem' }}>
|
| 299 |
+
<h2 style={{ fontSize: '1.5rem', fontWeight: '700', color: '#1f2937', marginBottom: '1rem' }}>
|
| 300 |
+
⚙️ Technical Details
|
| 301 |
+
</h2>
|
| 302 |
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
| 303 |
+
<div style={{
|
| 304 |
+
padding: '1rem',
|
| 305 |
+
background: 'rgba(59, 130, 246, 0.05)',
|
| 306 |
+
borderRadius: '8px',
|
| 307 |
+
borderLeft: '3px solid #3b82f6'
|
| 308 |
+
}}>
|
| 309 |
+
<h4 style={{ fontSize: '0.95rem', fontWeight: '600', marginBottom: '0.5rem', color: '#1f2937' }}>
|
| 310 |
+
Frontend
|
| 311 |
+
</h4>
|
| 312 |
+
<ul style={{ fontSize: '0.85rem', color: '#6b7280', lineHeight: '1.8', marginLeft: '1rem' }}>
|
| 313 |
+
<li>React 18</li>
|
| 314 |
+
<li>Vite</li>
|
| 315 |
+
<li>Recharts</li>
|
| 316 |
+
<li>Lucide Icons</li>
|
| 317 |
+
</ul>
|
| 318 |
+
</div>
|
| 319 |
+
<div style={{
|
| 320 |
+
padding: '1rem',
|
| 321 |
+
background: 'rgba(16, 185, 129, 0.05)',
|
| 322 |
+
borderRadius: '8px',
|
| 323 |
+
borderLeft: '3px solid #10b981'
|
| 324 |
+
}}>
|
| 325 |
+
<h4 style={{ fontSize: '0.95rem', fontWeight: '600', marginBottom: '0.5rem', color: '#1f2937' }}>
|
| 326 |
+
Backend
|
| 327 |
+
</h4>
|
| 328 |
+
<ul style={{ fontSize: '0.85rem', color: '#6b7280', lineHeight: '1.8', marginLeft: '1rem' }}>
|
| 329 |
+
<li>Node.js</li>
|
| 330 |
+
<li>Express</li>
|
| 331 |
+
<li>PDFKit</li>
|
| 332 |
+
<li>CSV Parser</li>
|
| 333 |
+
</ul>
|
| 334 |
+
</div>
|
| 335 |
+
</div>
|
| 336 |
+
</section>
|
| 337 |
+
|
| 338 |
+
{/* Support */}
|
| 339 |
+
<section style={{
|
| 340 |
+
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%)',
|
| 341 |
+
padding: '1.5rem',
|
| 342 |
+
borderRadius: '12px',
|
| 343 |
+
textAlign: 'center'
|
| 344 |
+
}}>
|
| 345 |
+
<h3 style={{ fontSize: '1.2rem', fontWeight: '700', marginBottom: '0.5rem', color: '#667eea' }}>
|
| 346 |
+
💡 Need Help?
|
| 347 |
+
</h3>
|
| 348 |
+
<p style={{ color: '#6b7280', fontSize: '0.95rem' }}>
|
| 349 |
+
Check the <strong>HOW_IT_WORKS.md</strong> file in the project root for detailed technical documentation.
|
| 350 |
+
</p>
|
| 351 |
+
</section>
|
| 352 |
+
</div>
|
| 353 |
+
</div>
|
| 354 |
+
</div>
|
| 355 |
+
)}
|
| 356 |
+
|
| 357 |
+
{/* Theme Details Modal */}
|
| 358 |
+
{showModal && selectedTheme && (
|
| 359 |
+
<div style={{
|
| 360 |
+
position: 'fixed',
|
| 361 |
+
top: 0,
|
| 362 |
+
left: 0,
|
| 363 |
+
right: 0,
|
| 364 |
+
bottom: 0,
|
| 365 |
+
background: 'rgba(0, 0, 0, 0.5)',
|
| 366 |
+
display: 'flex',
|
| 367 |
+
alignItems: 'center',
|
| 368 |
+
justifyContent: 'center',
|
| 369 |
+
zIndex: 1000,
|
| 370 |
+
backdropFilter: 'blur(4px)'
|
| 371 |
+
}}
|
| 372 |
+
onClick={() => setShowModal(false)}
|
| 373 |
+
>
|
| 374 |
+
<div style={{
|
| 375 |
+
background: 'white',
|
| 376 |
+
borderRadius: '16px',
|
| 377 |
+
padding: '2rem',
|
| 378 |
+
maxWidth: '500px',
|
| 379 |
+
width: '90%',
|
| 380 |
+
maxHeight: '80vh',
|
| 381 |
+
overflow: 'auto',
|
| 382 |
+
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
|
| 383 |
+
position: 'relative'
|
| 384 |
+
}}
|
| 385 |
+
onClick={(e) => e.stopPropagation()}
|
| 386 |
+
>
|
| 387 |
+
<button
|
| 388 |
+
onClick={() => setShowModal(false)}
|
| 389 |
+
style={{
|
| 390 |
+
position: 'absolute',
|
| 391 |
+
top: '1rem',
|
| 392 |
+
right: '1rem',
|
| 393 |
+
background: 'rgba(239, 68, 68, 0.1)',
|
| 394 |
+
border: 'none',
|
| 395 |
+
borderRadius: '50%',
|
| 396 |
+
width: '32px',
|
| 397 |
+
height: '32px',
|
| 398 |
+
display: 'flex',
|
| 399 |
+
alignItems: 'center',
|
| 400 |
+
justifyContent: 'center',
|
| 401 |
+
cursor: 'pointer',
|
| 402 |
+
transition: 'all 0.3s ease'
|
| 403 |
+
}}
|
| 404 |
+
onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(239, 68, 68, 0.2)'}
|
| 405 |
+
onMouseLeave={(e) => e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)'}
|
| 406 |
+
>
|
| 407 |
+
<X size={20} color="#ef4444" />
|
| 408 |
+
</button>
|
| 409 |
+
|
| 410 |
+
<h2 style={{
|
| 411 |
+
fontSize: '1.5rem',
|
| 412 |
+
fontWeight: '700',
|
| 413 |
+
color: '#1f2937',
|
| 414 |
+
marginBottom: '0.5rem'
|
| 415 |
+
}}>
|
| 416 |
+
🌊 {selectedTheme.theme}
|
| 417 |
+
</h2>
|
| 418 |
+
|
| 419 |
+
<p style={{
|
| 420 |
+
fontSize: '0.875rem',
|
| 421 |
+
color: '#6b7280',
|
| 422 |
+
marginBottom: '1.5rem'
|
| 423 |
+
}}>
|
| 424 |
+
Total Trainings: <strong style={{ color: '#667eea' }}>{selectedTheme.count}</strong>
|
| 425 |
+
</p>
|
| 426 |
+
|
| 427 |
+
<h3 style={{
|
| 428 |
+
fontSize: '1.1rem',
|
| 429 |
+
fontWeight: '600',
|
| 430 |
+
color: '#374151',
|
| 431 |
+
marginBottom: '1rem'
|
| 432 |
+
}}>
|
| 433 |
+
📍 States Covered ({selectedTheme.states.length}):
|
| 434 |
+
</h3>
|
| 435 |
+
|
| 436 |
+
{selectedTheme.states.length > 0 ? (
|
| 437 |
+
<div style={{
|
| 438 |
+
display: 'grid',
|
| 439 |
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
| 440 |
+
gap: '0.75rem'
|
| 441 |
+
}}>
|
| 442 |
+
{selectedTheme.states.map((item, idx) => (
|
| 443 |
+
<div key={idx} style={{
|
| 444 |
+
padding: '0.75rem 1rem',
|
| 445 |
+
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%)',
|
| 446 |
+
border: '1px solid rgba(102, 126, 234, 0.2)',
|
| 447 |
+
borderRadius: '8px',
|
| 448 |
+
fontSize: '0.9rem',
|
| 449 |
+
fontWeight: '500',
|
| 450 |
+
color: '#374151',
|
| 451 |
+
display: 'flex',
|
| 452 |
+
alignItems: 'center',
|
| 453 |
+
justifyContent: 'space-between',
|
| 454 |
+
gap: '0.5rem'
|
| 455 |
+
}}>
|
| 456 |
+
<span style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
| 457 |
+
<span style={{ color: '#667eea' }}>•</span>
|
| 458 |
+
{item.state}
|
| 459 |
+
</span>
|
| 460 |
+
<span style={{
|
| 461 |
+
background: '#667eea',
|
| 462 |
+
color: 'white',
|
| 463 |
+
padding: '0.25rem 0.5rem',
|
| 464 |
+
borderRadius: '12px',
|
| 465 |
+
fontSize: '0.75rem',
|
| 466 |
+
fontWeight: '700'
|
| 467 |
+
}}>
|
| 468 |
+
{item.count}
|
| 469 |
+
</span>
|
| 470 |
+
</div>
|
| 471 |
+
))}
|
| 472 |
+
</div>
|
| 473 |
+
) : (
|
| 474 |
+
<p style={{ color: '#6b7280', fontSize: '0.9rem' }}>
|
| 475 |
+
No state data available for this theme.
|
| 476 |
+
</p>
|
| 477 |
+
)}
|
| 478 |
+
|
| 479 |
+
<div style={{
|
| 480 |
+
marginTop: '1.5rem',
|
| 481 |
+
padding: '1rem',
|
| 482 |
+
background: 'rgba(59, 130, 246, 0.05)',
|
| 483 |
+
borderRadius: '8px',
|
| 484 |
+
borderLeft: '3px solid #3b82f6'
|
| 485 |
+
}}>
|
| 486 |
+
<p style={{
|
| 487 |
+
fontSize: '0.85rem',
|
| 488 |
+
color: '#374151',
|
| 489 |
+
lineHeight: '1.6'
|
| 490 |
+
}}>
|
| 491 |
+
💡 <strong>Insight:</strong> This disaster theme has been covered across multiple states, indicating comprehensive training coverage for {selectedTheme.theme.toLowerCase()} preparedness.
|
| 492 |
+
</p>
|
| 493 |
+
</div>
|
| 494 |
+
</div>
|
| 495 |
+
</div>
|
| 496 |
+
)}
|
| 497 |
+
|
| 498 |
+
{/* Rest of the dashboard content remains the same... */}
|
| 499 |
+
{/* Top Stats - Horizontal */}
|
| 500 |
+
<div style={{
|
| 501 |
+
display: 'grid',
|
| 502 |
+
gridTemplateColumns: 'repeat(4, 1fr)',
|
| 503 |
+
gap: '1rem',
|
| 504 |
+
marginBottom: '1.5rem'
|
| 505 |
+
}}>
|
| 506 |
+
<div className="glass-card">
|
| 507 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 508 |
+
<div style={{
|
| 509 |
+
width: '48px',
|
| 510 |
+
height: '48px',
|
| 511 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 512 |
+
borderRadius: '12px',
|
| 513 |
+
display: 'flex',
|
| 514 |
+
alignItems: 'center',
|
| 515 |
+
justifyContent: 'center',
|
| 516 |
+
flexShrink: 0
|
| 517 |
+
}}>
|
| 518 |
+
<Award size={24} color="white" />
|
| 519 |
+
</div>
|
| 520 |
+
<div>
|
| 521 |
+
<p style={{ fontSize: '0.75rem', color: '#6b7280', fontWeight: '600', marginBottom: '0.25rem' }}>
|
| 522 |
+
Total Trainings
|
| 523 |
+
</p>
|
| 524 |
+
<p style={{ fontSize: '1.75rem', fontWeight: '800', color: '#667eea' }}>
|
| 525 |
+
{analysis.totalTrainings || 0}
|
| 526 |
+
</p>
|
| 527 |
+
</div>
|
| 528 |
+
</div>
|
| 529 |
+
</div>
|
| 530 |
+
|
| 531 |
+
<div className="glass-card">
|
| 532 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 533 |
+
<div style={{
|
| 534 |
+
width: '48px',
|
| 535 |
+
height: '48px',
|
| 536 |
+
background: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
|
| 537 |
+
borderRadius: '12px',
|
| 538 |
+
display: 'flex',
|
| 539 |
+
alignItems: 'center',
|
| 540 |
+
justifyContent: 'center',
|
| 541 |
+
flexShrink: 0
|
| 542 |
+
}}>
|
| 543 |
+
<Users size={24} color="white" />
|
| 544 |
+
</div>
|
| 545 |
+
<div>
|
| 546 |
+
<p style={{ fontSize: '0.75rem', color: '#6b7280', fontWeight: '600', marginBottom: '0.25rem' }}>
|
| 547 |
+
Participants
|
| 548 |
+
</p>
|
| 549 |
+
<p style={{ fontSize: '1.75rem', fontWeight: '800', color: '#43e97b' }}>
|
| 550 |
+
{analysis.totalParticipants?.toLocaleString() || 0}
|
| 551 |
+
</p>
|
| 552 |
+
</div>
|
| 553 |
+
</div>
|
| 554 |
+
</div>
|
| 555 |
+
|
| 556 |
+
<div className="glass-card">
|
| 557 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 558 |
+
<div style={{
|
| 559 |
+
width: '48px',
|
| 560 |
+
height: '48px',
|
| 561 |
+
background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
| 562 |
+
borderRadius: '12px',
|
| 563 |
+
display: 'flex',
|
| 564 |
+
alignItems: 'center',
|
| 565 |
+
justifyContent: 'center',
|
| 566 |
+
flexShrink: 0
|
| 567 |
+
}}>
|
| 568 |
+
<TrendingUp size={24} color="white" />
|
| 569 |
+
</div>
|
| 570 |
+
<div>
|
| 571 |
+
<p style={{ fontSize: '0.75rem', color: '#6b7280', fontWeight: '600', marginBottom: '0.25rem' }}>
|
| 572 |
+
Completion
|
| 573 |
+
</p>
|
| 574 |
+
<p style={{ fontSize: '1.75rem', fontWeight: '800', color: '#f093fb' }}>
|
| 575 |
+
{analysis.averageCompletionRate || 'N/A'}
|
| 576 |
+
</p>
|
| 577 |
+
</div>
|
| 578 |
+
</div>
|
| 579 |
+
</div>
|
| 580 |
+
|
| 581 |
+
<div className="glass-card">
|
| 582 |
+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
|
| 583 |
+
<div style={{
|
| 584 |
+
width: '48px',
|
| 585 |
+
height: '48px',
|
| 586 |
+
background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
|
| 587 |
+
borderRadius: '12px',
|
| 588 |
+
display: 'flex',
|
| 589 |
+
alignItems: 'center',
|
| 590 |
+
justifyContent: 'center',
|
| 591 |
+
flexShrink: 0
|
| 592 |
+
}}>
|
| 593 |
+
<MapPin size={24} color="white" />
|
| 594 |
+
</div>
|
| 595 |
+
<div>
|
| 596 |
+
<p style={{ fontSize: '0.75rem', color: '#6b7280', fontWeight: '600', marginBottom: '0.25rem' }}>
|
| 597 |
+
States
|
| 598 |
+
</p>
|
| 599 |
+
<p style={{ fontSize: '1.75rem', fontWeight: '800', color: '#4facfe' }}>
|
| 600 |
+
{Object.keys(analysis.stateWiseCoverage || {}).length}
|
| 601 |
+
</p>
|
| 602 |
+
</div>
|
| 603 |
+
</div>
|
| 604 |
+
</div>
|
| 605 |
+
</div>
|
| 606 |
+
|
| 607 |
+
{/* Main Layout: Left (Charts) + Right (Business Insights) */}
|
| 608 |
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 380px', gap: '1.5rem', marginBottom: '1.5rem' }}>
|
| 609 |
+
{/* Left Side - Charts & Gap Analysis */}
|
| 610 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
|
| 611 |
+
{/* Charts Grid */}
|
| 612 |
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
|
| 613 |
+
{/* Theme Distribution - INTERACTIVE */}
|
| 614 |
+
{themeData.length > 0 && (
|
| 615 |
+
<div className="glass-panel" style={{ padding: '1.5rem' }}>
|
| 616 |
+
<h3 style={{ fontSize: '1.1rem', fontWeight: '700', marginBottom: '0.5rem', color: '#1f2937' }}>
|
| 617 |
+
📊 Theme Distribution
|
| 618 |
+
</h3>
|
| 619 |
+
<p style={{ fontSize: '0.75rem', color: '#6b7280', marginBottom: '1rem' }}>
|
| 620 |
+
Click on any theme to see details
|
| 621 |
+
</p>
|
| 622 |
+
<ResponsiveContainer width="100%" height={240}>
|
| 623 |
+
<PieChart>
|
| 624 |
+
<Pie
|
| 625 |
+
data={themeData}
|
| 626 |
+
cx="50%"
|
| 627 |
+
cy="50%"
|
| 628 |
+
labelLine={false}
|
| 629 |
+
label={({ name, percent }) => `${name.substring(0, 8)} ${(percent * 100).toFixed(0)}%`}
|
| 630 |
+
outerRadius={80}
|
| 631 |
+
fill="#8884d8"
|
| 632 |
+
dataKey="value"
|
| 633 |
+
onClick={handlePieClick}
|
| 634 |
+
style={{ cursor: 'pointer' }}
|
| 635 |
+
>
|
| 636 |
+
{themeData.map((entry, index) => (
|
| 637 |
+
<Cell
|
| 638 |
+
key={`cell-${index}`}
|
| 639 |
+
fill={COLORS[index % COLORS.length]}
|
| 640 |
+
style={{ cursor: 'pointer' }}
|
| 641 |
+
/>
|
| 642 |
+
))}
|
| 643 |
+
</Pie>
|
| 644 |
+
<Tooltip />
|
| 645 |
+
</PieChart>
|
| 646 |
+
</ResponsiveContainer>
|
| 647 |
+
</div>
|
| 648 |
+
)}
|
| 649 |
+
|
| 650 |
+
{/* State Coverage */}
|
| 651 |
+
{stateData.length > 0 && (
|
| 652 |
+
<div className="glass-panel" style={{ padding: '1.5rem' }}>
|
| 653 |
+
<h3 style={{ fontSize: '1.1rem', fontWeight: '700', marginBottom: '1rem', color: '#1f2937' }}>
|
| 654 |
+
📍 Top States
|
| 655 |
+
</h3>
|
| 656 |
+
<ResponsiveContainer width="100%" height={240}>
|
| 657 |
+
<BarChart data={stateData}>
|
| 658 |
+
<CartesianGrid strokeDasharray="3 3" stroke="rgba(102, 126, 234, 0.1)" />
|
| 659 |
+
<XAxis dataKey="name" tick={{ fontSize: 10 }} angle={-45} textAnchor="end" height={60} />
|
| 660 |
+
<YAxis tick={{ fontSize: 11 }} />
|
| 661 |
+
<Tooltip />
|
| 662 |
+
<Bar dataKey="value" fill="#667eea" radius={[6, 6, 0, 0]} />
|
| 663 |
+
</BarChart>
|
| 664 |
+
</ResponsiveContainer>
|
| 665 |
+
</div>
|
| 666 |
+
)}
|
| 667 |
+
</div>
|
| 668 |
+
|
| 669 |
+
{/* Gap Analysis */}
|
| 670 |
+
{analysis.gapAnalysis && (
|
| 671 |
+
<div className="glass-panel" style={{ padding: '1.5rem' }}>
|
| 672 |
+
<h3 style={{
|
| 673 |
+
fontSize: '1.25rem',
|
| 674 |
+
fontWeight: '700',
|
| 675 |
+
marginBottom: '1rem',
|
| 676 |
+
display: 'flex',
|
| 677 |
+
alignItems: 'center',
|
| 678 |
+
gap: '0.5rem',
|
| 679 |
+
color: '#1f2937'
|
| 680 |
+
}}>
|
| 681 |
+
<AlertTriangle size={24} color="#f093fb" />
|
| 682 |
+
Gap Analysis
|
| 683 |
+
</h3>
|
| 684 |
+
|
| 685 |
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '1rem' }}>
|
| 686 |
+
{analysis.gapAnalysis.underservedStates && analysis.gapAnalysis.underservedStates.length > 0 && (
|
| 687 |
+
<div>
|
| 688 |
+
<h4 style={{ fontSize: '0.9rem', fontWeight: '700', marginBottom: '0.75rem', color: '#f093fb' }}>
|
| 689 |
+
Underserved States
|
| 690 |
+
</h4>
|
| 691 |
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.4rem' }}>
|
| 692 |
+
{analysis.gapAnalysis.underservedStates.slice(0, 6).map((state, idx) => (
|
| 693 |
+
<span key={idx} style={{
|
| 694 |
+
padding: '0.4rem 0.75rem',
|
| 695 |
+
background: 'rgba(240, 147, 251, 0.1)',
|
| 696 |
+
border: '1px solid rgba(240, 147, 251, 0.3)',
|
| 697 |
+
borderRadius: '16px',
|
| 698 |
+
fontSize: '0.8rem',
|
| 699 |
+
fontWeight: '600',
|
| 700 |
+
color: '#f093fb'
|
| 701 |
+
}}>
|
| 702 |
+
{state}
|
| 703 |
+
</span>
|
| 704 |
+
))}
|
| 705 |
+
</div>
|
| 706 |
+
</div>
|
| 707 |
+
)}
|
| 708 |
+
|
| 709 |
+
{analysis.gapAnalysis.underservedThemes && analysis.gapAnalysis.underservedThemes.length > 0 && (
|
| 710 |
+
<div>
|
| 711 |
+
<h4 style={{ fontSize: '0.9rem', fontWeight: '700', marginBottom: '0.75rem', color: '#f5576c' }}>
|
| 712 |
+
Underserved Themes
|
| 713 |
+
</h4>
|
| 714 |
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.4rem' }}>
|
| 715 |
+
{analysis.gapAnalysis.underservedThemes.map((theme, idx) => (
|
| 716 |
+
<span key={idx} style={{
|
| 717 |
+
padding: '0.4rem 0.75rem',
|
| 718 |
+
background: 'rgba(245, 87, 108, 0.1)',
|
| 719 |
+
border: '1px solid rgba(245, 87, 108, 0.3)',
|
| 720 |
+
borderRadius: '16px',
|
| 721 |
+
fontSize: '0.8rem',
|
| 722 |
+
fontWeight: '600',
|
| 723 |
+
color: '#f5576c'
|
| 724 |
+
}}>
|
| 725 |
+
{theme}
|
| 726 |
+
</span>
|
| 727 |
+
))}
|
| 728 |
+
</div>
|
| 729 |
+
</div>
|
| 730 |
+
)}
|
| 731 |
+
|
| 732 |
+
{analysis.gapAnalysis.criticalGaps && analysis.gapAnalysis.criticalGaps.length > 0 && (
|
| 733 |
+
<div>
|
| 734 |
+
<h4 style={{ fontSize: '0.9rem', fontWeight: '700', marginBottom: '0.75rem', color: '#4facfe' }}>
|
| 735 |
+
Critical Gaps
|
| 736 |
+
</h4>
|
| 737 |
+
<ul style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
| 738 |
+
{analysis.gapAnalysis.criticalGaps.slice(0, 3).map((gap, idx) => (
|
| 739 |
+
<li key={idx} style={{
|
| 740 |
+
padding: '0.5rem',
|
| 741 |
+
background: 'rgba(79, 172, 254, 0.05)',
|
| 742 |
+
borderLeft: '3px solid #4facfe',
|
| 743 |
+
marginBottom: '0.4rem',
|
| 744 |
+
borderRadius: '6px',
|
| 745 |
+
fontSize: '0.8rem',
|
| 746 |
+
color: '#374151'
|
| 747 |
+
}}>
|
| 748 |
+
{gap}
|
| 749 |
+
</li>
|
| 750 |
+
))}
|
| 751 |
+
</ul>
|
| 752 |
+
</div>
|
| 753 |
+
)}
|
| 754 |
+
</div>
|
| 755 |
+
</div>
|
| 756 |
+
)}
|
| 757 |
+
</div>
|
| 758 |
+
|
| 759 |
+
{/* Right Side - Business Insights */}
|
| 760 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
|
| 761 |
+
{analysis.keyInsights && analysis.keyInsights.length > 0 && (
|
| 762 |
+
<div className="glass-panel" style={{ padding: '1.5rem' }}>
|
| 763 |
+
<h3 style={{
|
| 764 |
+
fontSize: '1.25rem',
|
| 765 |
+
fontWeight: '700',
|
| 766 |
+
marginBottom: '1rem',
|
| 767 |
+
display: 'flex',
|
| 768 |
+
alignItems: 'center',
|
| 769 |
+
gap: '0.5rem',
|
| 770 |
+
color: '#1f2937'
|
| 771 |
+
}}>
|
| 772 |
+
<Lightbulb size={24} color="#667eea" />
|
| 773 |
+
Business Insights
|
| 774 |
+
</h3>
|
| 775 |
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
| 776 |
+
{analysis.keyInsights.map((insight, idx) => (
|
| 777 |
+
<div key={idx} style={{
|
| 778 |
+
padding: '0.875rem',
|
| 779 |
+
background: 'linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%)',
|
| 780 |
+
borderLeft: '3px solid #667eea',
|
| 781 |
+
borderRadius: '8px',
|
| 782 |
+
fontSize: '0.85rem',
|
| 783 |
+
lineHeight: '1.6',
|
| 784 |
+
color: '#374151'
|
| 785 |
+
}}>
|
| 786 |
+
<strong style={{ color: '#667eea' }}>•</strong> {insight}
|
| 787 |
+
</div>
|
| 788 |
+
))}
|
| 789 |
+
</div>
|
| 790 |
+
</div>
|
| 791 |
+
)}
|
| 792 |
+
</div>
|
| 793 |
+
</div>
|
| 794 |
+
|
| 795 |
+
{/* Recommendations - Full Width */}
|
| 796 |
+
{analysis.recommendations && analysis.recommendations.length > 0 && (
|
| 797 |
+
<div className="glass-panel" style={{ padding: '1.5rem' }}>
|
| 798 |
+
<h3 style={{
|
| 799 |
+
fontSize: '1.25rem',
|
| 800 |
+
fontWeight: '700',
|
| 801 |
+
marginBottom: '1rem',
|
| 802 |
+
display: 'flex',
|
| 803 |
+
alignItems: 'center',
|
| 804 |
+
gap: '0.5rem',
|
| 805 |
+
color: '#1f2937'
|
| 806 |
+
}}>
|
| 807 |
+
<Target size={24} color="#43e97b" />
|
| 808 |
+
Recommendations
|
| 809 |
+
</h3>
|
| 810 |
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '1rem' }}>
|
| 811 |
+
{analysis.recommendations.map((recommendation, idx) => (
|
| 812 |
+
<div key={idx} style={{
|
| 813 |
+
padding: '1rem',
|
| 814 |
+
background: 'linear-gradient(135deg, rgba(67, 233, 123, 0.05) 0%, rgba(56, 249, 215, 0.05) 100%)',
|
| 815 |
+
borderLeft: '3px solid #43e97b',
|
| 816 |
+
borderRadius: '8px',
|
| 817 |
+
fontSize: '0.9rem',
|
| 818 |
+
lineHeight: '1.6',
|
| 819 |
+
color: '#374151',
|
| 820 |
+
transition: 'all 0.3s ease'
|
| 821 |
+
}}
|
| 822 |
+
onMouseEnter={(e) => {
|
| 823 |
+
e.currentTarget.style.transform = 'translateY(-4px)';
|
| 824 |
+
e.currentTarget.style.boxShadow = '0 4px 12px rgba(67, 233, 123, 0.2)';
|
| 825 |
+
}}
|
| 826 |
+
onMouseLeave={(e) => {
|
| 827 |
+
e.currentTarget.style.transform = 'translateY(0)';
|
| 828 |
+
e.currentTarget.style.boxShadow = 'none';
|
| 829 |
+
}}
|
| 830 |
+
>
|
| 831 |
+
<strong style={{ color: '#43e97b' }}>✓</strong> {recommendation}
|
| 832 |
+
</div>
|
| 833 |
+
))}
|
| 834 |
+
</div>
|
| 835 |
+
</div>
|
| 836 |
+
)}
|
| 837 |
+
</div>
|
| 838 |
+
);
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
export default AnalysisDashboard;
|
frontend/src/components/FileUpload.jsx
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useCallback } from 'react';
|
| 2 |
+
import { useDropzone } from 'react-dropzone';
|
| 3 |
+
import axios from 'axios';
|
| 4 |
+
import { Upload, FileText, X, CheckCircle, AlertCircle, Loader } from 'lucide-react';
|
| 5 |
+
|
| 6 |
+
function FileUpload({ onAnalysisComplete }) {
|
| 7 |
+
const [file, setFile] = useState(null);
|
| 8 |
+
const [uploading, setUploading] = useState(false);
|
| 9 |
+
const [progress, setProgress] = useState(0);
|
| 10 |
+
const [error, setError] = useState(null);
|
| 11 |
+
const [success, setSuccess] = useState(false);
|
| 12 |
+
|
| 13 |
+
const onDrop = useCallback((acceptedFiles) => {
|
| 14 |
+
if (acceptedFiles.length > 0) {
|
| 15 |
+
setFile(acceptedFiles[0]);
|
| 16 |
+
setError(null);
|
| 17 |
+
setSuccess(false);
|
| 18 |
+
}
|
| 19 |
+
}, []);
|
| 20 |
+
|
| 21 |
+
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
| 22 |
+
onDrop,
|
| 23 |
+
accept: {
|
| 24 |
+
'application/pdf': ['.pdf'],
|
| 25 |
+
'application/vnd.ms-excel': ['.xls'],
|
| 26 |
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
|
| 27 |
+
'text/csv': ['.csv']
|
| 28 |
+
},
|
| 29 |
+
maxFiles: 1,
|
| 30 |
+
maxSize: 10 * 1024 * 1024 // 10MB
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
const handleUpload = async () => {
|
| 34 |
+
if (!file) return;
|
| 35 |
+
|
| 36 |
+
setUploading(true);
|
| 37 |
+
setError(null);
|
| 38 |
+
setProgress(0);
|
| 39 |
+
|
| 40 |
+
const formData = new FormData();
|
| 41 |
+
formData.append('file', file);
|
| 42 |
+
|
| 43 |
+
try {
|
| 44 |
+
const response = await axios.post('/api/upload', formData, {
|
| 45 |
+
headers: {
|
| 46 |
+
'Content-Type': 'multipart/form-data'
|
| 47 |
+
},
|
| 48 |
+
onUploadProgress: (progressEvent) => {
|
| 49 |
+
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
| 50 |
+
setProgress(percentCompleted);
|
| 51 |
+
}
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
setSuccess(true);
|
| 55 |
+
setUploading(false);
|
| 56 |
+
|
| 57 |
+
// Pass analysis data to parent
|
| 58 |
+
if (onAnalysisComplete) {
|
| 59 |
+
onAnalysisComplete(response.data);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
} catch (err) {
|
| 63 |
+
setError(err.response?.data?.message || 'Upload failed. Please try again.');
|
| 64 |
+
setUploading(false);
|
| 65 |
+
setProgress(0);
|
| 66 |
+
}
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
const removeFile = () => {
|
| 70 |
+
setFile(null);
|
| 71 |
+
setError(null);
|
| 72 |
+
setSuccess(false);
|
| 73 |
+
setProgress(0);
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
return (
|
| 77 |
+
<div className="fade-in">
|
| 78 |
+
<div className="glass-panel" style={{ maxWidth: '800px', margin: '0 auto' }}>
|
| 79 |
+
<div className="text-center mb-4">
|
| 80 |
+
<h2 style={{ fontSize: '2rem', fontWeight: '700', marginBottom: '0.5rem' }}>
|
| 81 |
+
Upload Training Data
|
| 82 |
+
</h2>
|
| 83 |
+
<p style={{ color: 'var(--text-secondary)', fontSize: '1rem' }}>
|
| 84 |
+
Upload PDF, Excel, or CSV files containing disaster management training data
|
| 85 |
+
</p>
|
| 86 |
+
</div>
|
| 87 |
+
|
| 88 |
+
{/* Dropzone */}
|
| 89 |
+
<div
|
| 90 |
+
{...getRootProps()}
|
| 91 |
+
style={{
|
| 92 |
+
border: `3px dashed ${isDragActive ? 'var(--primary-light)' : 'var(--border)'}`,
|
| 93 |
+
borderRadius: 'var(--radius-xl)',
|
| 94 |
+
padding: '3rem 2rem',
|
| 95 |
+
textAlign: 'center',
|
| 96 |
+
cursor: 'pointer',
|
| 97 |
+
background: isDragActive ? 'rgba(59, 130, 246, 0.05)' : 'var(--bg-secondary)',
|
| 98 |
+
transition: 'all var(--transition-normal)',
|
| 99 |
+
marginBottom: '1.5rem'
|
| 100 |
+
}}
|
| 101 |
+
>
|
| 102 |
+
<input {...getInputProps()} />
|
| 103 |
+
|
| 104 |
+
<div style={{
|
| 105 |
+
width: '80px',
|
| 106 |
+
height: '80px',
|
| 107 |
+
margin: '0 auto 1rem',
|
| 108 |
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 109 |
+
borderRadius: '50%',
|
| 110 |
+
display: 'flex',
|
| 111 |
+
alignItems: 'center',
|
| 112 |
+
justifyContent: 'center',
|
| 113 |
+
color: 'white'
|
| 114 |
+
}}>
|
| 115 |
+
<Upload size={40} />
|
| 116 |
+
</div>
|
| 117 |
+
|
| 118 |
+
{isDragActive ? (
|
| 119 |
+
<p style={{ fontSize: '1.1rem', fontWeight: '600', color: 'var(--primary)' }}>
|
| 120 |
+
Drop the file here...
|
| 121 |
+
</p>
|
| 122 |
+
) : (
|
| 123 |
+
<>
|
| 124 |
+
<p style={{ fontSize: '1.1rem', fontWeight: '600', marginBottom: '0.5rem' }}>
|
| 125 |
+
Drag & drop your file here
|
| 126 |
+
</p>
|
| 127 |
+
<p style={{ color: 'var(--text-secondary)', fontSize: '0.95rem' }}>
|
| 128 |
+
or click to browse files
|
| 129 |
+
</p>
|
| 130 |
+
<p style={{ color: 'var(--text-tertiary)', fontSize: '0.875rem', marginTop: '1rem' }}>
|
| 131 |
+
Supported formats: PDF, Excel (.xlsx, .xls), CSV (Max 10MB)
|
| 132 |
+
</p>
|
| 133 |
+
</>
|
| 134 |
+
)}
|
| 135 |
+
</div>
|
| 136 |
+
|
| 137 |
+
{/* Selected File */}
|
| 138 |
+
{file && (
|
| 139 |
+
<div className="glass-card" style={{ marginBottom: '1.5rem' }}>
|
| 140 |
+
<div className="flex items-center justify-between">
|
| 141 |
+
<div className="flex items-center gap-3">
|
| 142 |
+
<div style={{
|
| 143 |
+
width: '48px',
|
| 144 |
+
height: '48px',
|
| 145 |
+
background: 'linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%)',
|
| 146 |
+
borderRadius: 'var(--radius-md)',
|
| 147 |
+
display: 'flex',
|
| 148 |
+
alignItems: 'center',
|
| 149 |
+
justifyContent: 'center',
|
| 150 |
+
color: 'white'
|
| 151 |
+
}}>
|
| 152 |
+
<FileText size={24} />
|
| 153 |
+
</div>
|
| 154 |
+
<div>
|
| 155 |
+
<p style={{ fontWeight: '600', fontSize: '1rem' }}>{file.name}</p>
|
| 156 |
+
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem' }}>
|
| 157 |
+
{(file.size / 1024 / 1024).toFixed(2)} MB
|
| 158 |
+
</p>
|
| 159 |
+
</div>
|
| 160 |
+
</div>
|
| 161 |
+
<button
|
| 162 |
+
onClick={removeFile}
|
| 163 |
+
style={{
|
| 164 |
+
background: 'rgba(239, 68, 68, 0.1)',
|
| 165 |
+
color: 'var(--error)',
|
| 166 |
+
border: 'none',
|
| 167 |
+
borderRadius: '50%',
|
| 168 |
+
width: '36px',
|
| 169 |
+
height: '36px',
|
| 170 |
+
display: 'flex',
|
| 171 |
+
alignItems: 'center',
|
| 172 |
+
justifyContent: 'center',
|
| 173 |
+
cursor: 'pointer',
|
| 174 |
+
transition: 'all var(--transition-fast)'
|
| 175 |
+
}}
|
| 176 |
+
>
|
| 177 |
+
<X size={20} />
|
| 178 |
+
</button>
|
| 179 |
+
</div>
|
| 180 |
+
</div>
|
| 181 |
+
)}
|
| 182 |
+
|
| 183 |
+
{/* Progress Bar */}
|
| 184 |
+
{uploading && (
|
| 185 |
+
<div style={{ marginBottom: '1.5rem' }}>
|
| 186 |
+
<div className="flex items-center justify-between mb-2">
|
| 187 |
+
<span style={{ fontSize: '0.875rem', fontWeight: '600' }}>
|
| 188 |
+
Analyzing document...
|
| 189 |
+
</span>
|
| 190 |
+
<span style={{ fontSize: '0.875rem', color: 'var(--primary)' }}>
|
| 191 |
+
{progress}%
|
| 192 |
+
</span>
|
| 193 |
+
</div>
|
| 194 |
+
<div style={{
|
| 195 |
+
width: '100%',
|
| 196 |
+
height: '8px',
|
| 197 |
+
background: 'var(--bg-tertiary)',
|
| 198 |
+
borderRadius: 'var(--radius-lg)',
|
| 199 |
+
overflow: 'hidden'
|
| 200 |
+
}}>
|
| 201 |
+
<div style={{
|
| 202 |
+
width: `${progress}%`,
|
| 203 |
+
height: '100%',
|
| 204 |
+
background: 'linear-gradient(90deg, var(--primary) 0%, var(--primary-light) 100%)',
|
| 205 |
+
transition: 'width 0.3s ease',
|
| 206 |
+
borderRadius: 'var(--radius-lg)'
|
| 207 |
+
}} />
|
| 208 |
+
</div>
|
| 209 |
+
<div className="flex items-center gap-2 mt-3" style={{ color: 'var(--text-secondary)' }}>
|
| 210 |
+
<Loader size={16} className="spinner" style={{ width: '16px', height: '16px', borderWidth: '2px' }} />
|
| 211 |
+
<span style={{ fontSize: '0.875rem' }}>
|
| 212 |
+
Processing with AI... This may take a minute
|
| 213 |
+
</span>
|
| 214 |
+
</div>
|
| 215 |
+
</div>
|
| 216 |
+
)}
|
| 217 |
+
|
| 218 |
+
{/* Error Message */}
|
| 219 |
+
{error && (
|
| 220 |
+
<div className="flex items-center gap-2 mb-3" style={{
|
| 221 |
+
padding: '1rem',
|
| 222 |
+
background: 'rgba(239, 68, 68, 0.1)',
|
| 223 |
+
border: '1px solid rgba(239, 68, 68, 0.3)',
|
| 224 |
+
borderRadius: 'var(--radius-md)',
|
| 225 |
+
color: 'var(--error)'
|
| 226 |
+
}}>
|
| 227 |
+
<AlertCircle size={20} />
|
| 228 |
+
<span>{error}</span>
|
| 229 |
+
</div>
|
| 230 |
+
)}
|
| 231 |
+
|
| 232 |
+
{/* Success Message */}
|
| 233 |
+
{success && (
|
| 234 |
+
<div className="flex items-center gap-2 mb-3" style={{
|
| 235 |
+
padding: '1rem',
|
| 236 |
+
background: 'rgba(16, 185, 129, 0.1)',
|
| 237 |
+
border: '1px solid rgba(16, 185, 129, 0.3)',
|
| 238 |
+
borderRadius: 'var(--radius-md)',
|
| 239 |
+
color: 'var(--success)'
|
| 240 |
+
}}>
|
| 241 |
+
<CheckCircle size={20} />
|
| 242 |
+
<span>Analysis completed successfully! View the dashboard to see insights.</span>
|
| 243 |
+
</div>
|
| 244 |
+
)}
|
| 245 |
+
|
| 246 |
+
{/* Upload Button */}
|
| 247 |
+
<button
|
| 248 |
+
onClick={handleUpload}
|
| 249 |
+
disabled={!file || uploading}
|
| 250 |
+
className="btn btn-primary"
|
| 251 |
+
style={{ width: '100%', justifyContent: 'center', fontSize: '1rem', padding: '1rem' }}
|
| 252 |
+
>
|
| 253 |
+
{uploading ? (
|
| 254 |
+
<>
|
| 255 |
+
<Loader size={20} className="spinner" style={{ width: '20px', height: '20px', borderWidth: '2px' }} />
|
| 256 |
+
Analyzing...
|
| 257 |
+
</>
|
| 258 |
+
) : (
|
| 259 |
+
<>
|
| 260 |
+
<Upload size={20} />
|
| 261 |
+
Upload & Analyze
|
| 262 |
+
</>
|
| 263 |
+
)}
|
| 264 |
+
</button>
|
| 265 |
+
</div>
|
| 266 |
+
</div>
|
| 267 |
+
);
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
export default FileUpload;
|
frontend/src/components/ReportPreview.jsx
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Download, FileText, CheckCircle } from 'lucide-react';
|
| 2 |
+
|
| 3 |
+
function ReportPreview({ data }) {
|
| 4 |
+
const { reportFilename, analysis, executiveSummary } = data;
|
| 5 |
+
|
| 6 |
+
const handleDownload = () => {
|
| 7 |
+
window.open(`/api/analysis/report/${reportFilename}`, '_blank');
|
| 8 |
+
};
|
| 9 |
+
|
| 10 |
+
return (
|
| 11 |
+
<div className="fade-in">
|
| 12 |
+
<div className="glass-panel" style={{ maxWidth: '900px', margin: '0 auto' }}>
|
| 13 |
+
<div className="text-center mb-4">
|
| 14 |
+
<div style={{
|
| 15 |
+
width: '80px',
|
| 16 |
+
height: '80px',
|
| 17 |
+
margin: '0 auto 1.5rem',
|
| 18 |
+
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
| 19 |
+
borderRadius: '50%',
|
| 20 |
+
display: 'flex',
|
| 21 |
+
alignItems: 'center',
|
| 22 |
+
justifyContent: 'center',
|
| 23 |
+
color: 'white'
|
| 24 |
+
}}>
|
| 25 |
+
<CheckCircle size={40} />
|
| 26 |
+
</div>
|
| 27 |
+
|
| 28 |
+
<h2 style={{ fontSize: '2rem', fontWeight: '700', marginBottom: '0.5rem' }}>
|
| 29 |
+
Report Generated Successfully
|
| 30 |
+
</h2>
|
| 31 |
+
<p style={{ color: 'var(--text-secondary)', fontSize: '1rem' }}>
|
| 32 |
+
Your government-format analysis report is ready for download
|
| 33 |
+
</p>
|
| 34 |
+
</div>
|
| 35 |
+
|
| 36 |
+
{/* Report Preview */}
|
| 37 |
+
<div style={{
|
| 38 |
+
background: 'var(--bg-secondary)',
|
| 39 |
+
border: '2px solid var(--border)',
|
| 40 |
+
borderRadius: 'var(--radius-lg)',
|
| 41 |
+
padding: '2rem',
|
| 42 |
+
marginBottom: '2rem'
|
| 43 |
+
}}>
|
| 44 |
+
<div style={{
|
| 45 |
+
borderBottom: '3px solid var(--primary)',
|
| 46 |
+
paddingBottom: '1.5rem',
|
| 47 |
+
marginBottom: '1.5rem',
|
| 48 |
+
textAlign: 'center'
|
| 49 |
+
}}>
|
| 50 |
+
<h3 style={{ fontSize: '1.5rem', fontWeight: '700', color: 'var(--primary)' }}>
|
| 51 |
+
GOVERNMENT OF INDIA
|
| 52 |
+
</h3>
|
| 53 |
+
<p style={{ fontSize: '1.2rem', fontWeight: '600', marginTop: '0.5rem' }}>
|
| 54 |
+
National Disaster Management Authority
|
| 55 |
+
</p>
|
| 56 |
+
<p style={{ fontSize: '0.9rem', color: 'var(--text-secondary)', marginTop: '0.25rem' }}>
|
| 57 |
+
(Ministry of Home Affairs)
|
| 58 |
+
</p>
|
| 59 |
+
</div>
|
| 60 |
+
|
| 61 |
+
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
|
| 62 |
+
<h4 style={{ fontSize: '1.3rem', fontWeight: '700', marginBottom: '0.5rem' }}>
|
| 63 |
+
DISASTER MANAGEMENT TRAINING
|
| 64 |
+
</h4>
|
| 65 |
+
<h4 style={{ fontSize: '1.2rem', fontWeight: '600', color: 'var(--text-secondary)' }}>
|
| 66 |
+
ANALYSIS REPORT
|
| 67 |
+
</h4>
|
| 68 |
+
</div>
|
| 69 |
+
|
| 70 |
+
<div style={{ marginBottom: '1.5rem' }}>
|
| 71 |
+
<h5 style={{ fontSize: '1.1rem', fontWeight: '600', marginBottom: '0.75rem', color: 'var(--primary)' }}>
|
| 72 |
+
EXECUTIVE SUMMARY
|
| 73 |
+
</h5>
|
| 74 |
+
<p style={{ lineHeight: '1.8', color: 'var(--text-secondary)', textAlign: 'justify', fontSize: '0.95rem' }}>
|
| 75 |
+
{executiveSummary ? executiveSummary.substring(0, 300) + '...' : 'Analysis summary not available'}
|
| 76 |
+
</p>
|
| 77 |
+
</div>
|
| 78 |
+
|
| 79 |
+
<div style={{ marginBottom: '1.5rem' }}>
|
| 80 |
+
<h5 style={{ fontSize: '1.1rem', fontWeight: '600', marginBottom: '0.75rem', color: 'var(--primary)' }}>
|
| 81 |
+
KEY METRICS
|
| 82 |
+
</h5>
|
| 83 |
+
<div className="grid grid-cols-2 gap-3">
|
| 84 |
+
<div style={{ padding: '0.75rem', background: 'var(--bg-tertiary)', borderRadius: 'var(--radius-md)' }}>
|
| 85 |
+
<p style={{ fontSize: '0.875rem', color: 'var(--text-secondary)' }}>Total Training Sessions</p>
|
| 86 |
+
<p style={{ fontSize: '1.5rem', fontWeight: '700', color: 'var(--primary)' }}>
|
| 87 |
+
{analysis.totalTrainings || 'N/A'}
|
| 88 |
+
</p>
|
| 89 |
+
</div>
|
| 90 |
+
<div style={{ padding: '0.75rem', background: 'var(--bg-tertiary)', borderRadius: 'var(--radius-md)' }}>
|
| 91 |
+
<p style={{ fontSize: '0.875rem', color: 'var(--text-secondary)' }}>Total Participants</p>
|
| 92 |
+
<p style={{ fontSize: '1.5rem', fontWeight: '700', color: 'var(--secondary)' }}>
|
| 93 |
+
{analysis.totalParticipants || 'N/A'}
|
| 94 |
+
</p>
|
| 95 |
+
</div>
|
| 96 |
+
</div>
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
<div style={{
|
| 100 |
+
borderTop: '2px solid var(--border)',
|
| 101 |
+
paddingTop: '1rem',
|
| 102 |
+
marginTop: '2rem',
|
| 103 |
+
textAlign: 'center',
|
| 104 |
+
fontSize: '0.875rem',
|
| 105 |
+
color: 'var(--text-tertiary)'
|
| 106 |
+
}}>
|
| 107 |
+
<p>© National Disaster Management Authority, Government of India</p>
|
| 108 |
+
<p style={{ marginTop: '0.25rem', fontSize: '0.8rem', fontStyle: 'italic' }}>
|
| 109 |
+
CONFIDENTIAL - For Official Use Only
|
| 110 |
+
</p>
|
| 111 |
+
</div>
|
| 112 |
+
</div>
|
| 113 |
+
|
| 114 |
+
{/* Download Button */}
|
| 115 |
+
<div className="text-center">
|
| 116 |
+
<button
|
| 117 |
+
onClick={handleDownload}
|
| 118 |
+
className="btn btn-success"
|
| 119 |
+
style={{ fontSize: '1.1rem', padding: '1rem 2.5rem' }}
|
| 120 |
+
>
|
| 121 |
+
<Download size={24} />
|
| 122 |
+
Download Full Report (PDF)
|
| 123 |
+
</button>
|
| 124 |
+
|
| 125 |
+
<p style={{ marginTop: '1rem', color: 'var(--text-secondary)', fontSize: '0.9rem' }}>
|
| 126 |
+
The complete report includes detailed analysis, charts, gap analysis, and recommendations
|
| 127 |
+
</p>
|
| 128 |
+
</div>
|
| 129 |
+
</div>
|
| 130 |
+
</div>
|
| 131 |
+
);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
export default ReportPreview;
|
frontend/src/index.css
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
| 2 |
+
|
| 3 |
+
* {
|
| 4 |
+
margin: 0;
|
| 5 |
+
padding: 0;
|
| 6 |
+
box-sizing: border-box;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
:root {
|
| 10 |
+
--gradient-1: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 11 |
+
--gradient-2: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 12 |
+
--gradient-3: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
| 13 |
+
--gradient-4: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
| 14 |
+
|
| 15 |
+
--primary: #3b82f6;
|
| 16 |
+
--secondary: #10b981;
|
| 17 |
+
--accent: #f59e0b;
|
| 18 |
+
--error: #ef4444;
|
| 19 |
+
--warning: #f59e0b;
|
| 20 |
+
--success: #10b981;
|
| 21 |
+
--info: #3b82f6;
|
| 22 |
+
|
| 23 |
+
--blue-500: #3b82f6;
|
| 24 |
+
--blue-600: #2563eb;
|
| 25 |
+
--blue-700: #1d4ed8;
|
| 26 |
+
|
| 27 |
+
--purple-500: #8b5cf6;
|
| 28 |
+
--purple-600: #7c3aed;
|
| 29 |
+
|
| 30 |
+
--pink-500: #ec4899;
|
| 31 |
+
--pink-600: #db2777;
|
| 32 |
+
|
| 33 |
+
--text-primary: #1f2937;
|
| 34 |
+
--text-secondary: #6b7280;
|
| 35 |
+
|
| 36 |
+
--radius-sm: 0.375rem;
|
| 37 |
+
--radius-lg: 0.75rem;
|
| 38 |
+
|
| 39 |
+
--gray-50: #f9fafb;
|
| 40 |
+
--gray-100: #f3f4f6;
|
| 41 |
+
--gray-200: #e5e7eb;
|
| 42 |
+
--gray-300: #d1d5db;
|
| 43 |
+
--gray-400: #9ca3af;
|
| 44 |
+
--gray-500: #6b7280;
|
| 45 |
+
--gray-600: #4b5563;
|
| 46 |
+
--gray-700: #374151;
|
| 47 |
+
--gray-800: #1f2937;
|
| 48 |
+
--gray-900: #111827;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
body {
|
| 52 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
| 53 |
+
-webkit-font-smoothing: antialiased;
|
| 54 |
+
-moz-osx-font-smoothing: grayscale;
|
| 55 |
+
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 50%, #ffffff 100%);
|
| 56 |
+
background-attachment: fixed;
|
| 57 |
+
min-height: 100vh;
|
| 58 |
+
color: #1f2937;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.container {
|
| 62 |
+
max-width: 1280px;
|
| 63 |
+
margin: 0 auto;
|
| 64 |
+
padding: 0 1.5rem;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/* Premium Glass Morphism */
|
| 68 |
+
.glass-panel {
|
| 69 |
+
background: rgba(255, 255, 255, 0.95);
|
| 70 |
+
backdrop-filter: blur(30px) saturate(180%);
|
| 71 |
+
-webkit-backdrop-filter: blur(30px) saturate(180%);
|
| 72 |
+
border-radius: 24px;
|
| 73 |
+
border: 1px solid rgba(255, 255, 255, 0.5);
|
| 74 |
+
box-shadow:
|
| 75 |
+
0 20px 60px rgba(0, 0, 0, 0.15),
|
| 76 |
+
0 8px 16px rgba(0, 0, 0, 0.1),
|
| 77 |
+
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
| 78 |
+
padding: 2.5rem;
|
| 79 |
+
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
| 80 |
+
position: relative;
|
| 81 |
+
overflow: hidden;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.glass-panel::before {
|
| 85 |
+
content: '';
|
| 86 |
+
position: absolute;
|
| 87 |
+
top: 0;
|
| 88 |
+
left: -100%;
|
| 89 |
+
width: 100%;
|
| 90 |
+
height: 100%;
|
| 91 |
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
| 92 |
+
transition: left 0.7s;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.glass-panel:hover::before {
|
| 96 |
+
left: 100%;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.glass-panel:hover {
|
| 100 |
+
transform: translateY(-8px);
|
| 101 |
+
box-shadow:
|
| 102 |
+
0 30px 80px rgba(0, 0, 0, 0.2),
|
| 103 |
+
0 12px 24px rgba(0, 0, 0, 0.15),
|
| 104 |
+
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/* Premium Cards */
|
| 108 |
+
.glass-card {
|
| 109 |
+
background: rgba(255, 255, 255, 0.9);
|
| 110 |
+
backdrop-filter: blur(20px);
|
| 111 |
+
border-radius: 16px;
|
| 112 |
+
border: 1px solid rgba(255, 255, 255, 0.4);
|
| 113 |
+
padding: 1.75rem;
|
| 114 |
+
box-shadow:
|
| 115 |
+
0 10px 30px rgba(0, 0, 0, 0.1),
|
| 116 |
+
0 4px 8px rgba(0, 0, 0, 0.05);
|
| 117 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 118 |
+
cursor: pointer;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.glass-card:hover {
|
| 122 |
+
transform: translateY(-6px) scale(1.02);
|
| 123 |
+
box-shadow:
|
| 124 |
+
0 20px 50px rgba(0, 0, 0, 0.15),
|
| 125 |
+
0 8px 16px rgba(59, 130, 246, 0.2);
|
| 126 |
+
border-color: rgba(59, 130, 246, 0.5);
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/* Premium Buttons */
|
| 130 |
+
.btn {
|
| 131 |
+
padding: 0.875rem 1.75rem;
|
| 132 |
+
border-radius: 12px;
|
| 133 |
+
border: none;
|
| 134 |
+
font-weight: 600;
|
| 135 |
+
font-size: 0.95rem;
|
| 136 |
+
cursor: pointer;
|
| 137 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 138 |
+
display: inline-flex;
|
| 139 |
+
align-items: center;
|
| 140 |
+
gap: 0.625rem;
|
| 141 |
+
position: relative;
|
| 142 |
+
overflow: hidden;
|
| 143 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
.btn::before {
|
| 147 |
+
content: '';
|
| 148 |
+
position: absolute;
|
| 149 |
+
top: 50%;
|
| 150 |
+
left: 50%;
|
| 151 |
+
width: 0;
|
| 152 |
+
height: 0;
|
| 153 |
+
border-radius: 50%;
|
| 154 |
+
background: rgba(255, 255, 255, 0.4);
|
| 155 |
+
transform: translate(-50%, -50%);
|
| 156 |
+
transition: width 0.6s, height 0.6s;
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
.btn:hover::before {
|
| 160 |
+
width: 400px;
|
| 161 |
+
height: 400px;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.btn-primary {
|
| 165 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 166 |
+
color: white;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.btn-primary:hover {
|
| 170 |
+
transform: translateY(-3px);
|
| 171 |
+
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.btn-primary:active {
|
| 175 |
+
transform: translateY(-1px);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.btn-secondary {
|
| 179 |
+
background: rgba(255, 255, 255, 0.95);
|
| 180 |
+
color: #667eea;
|
| 181 |
+
border: 2px solid rgba(102, 126, 234, 0.3);
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.btn-secondary:hover {
|
| 185 |
+
background: rgba(102, 126, 234, 0.1);
|
| 186 |
+
border-color: #667eea;
|
| 187 |
+
transform: translateY(-2px);
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.btn:disabled {
|
| 191 |
+
opacity: 0.6;
|
| 192 |
+
cursor: not-allowed;
|
| 193 |
+
transform: none !important;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
/* Input Fields */
|
| 197 |
+
.glass-input {
|
| 198 |
+
background: rgba(255, 255, 255, 0.95);
|
| 199 |
+
border: 2px solid rgba(102, 126, 234, 0.2);
|
| 200 |
+
border-radius: 12px;
|
| 201 |
+
padding: 0.875rem 1.25rem;
|
| 202 |
+
font-size: 1rem;
|
| 203 |
+
color: #1f2937;
|
| 204 |
+
transition: all 0.3s ease;
|
| 205 |
+
width: 100%;
|
| 206 |
+
font-family: 'Inter', sans-serif;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.glass-input:focus {
|
| 210 |
+
outline: none;
|
| 211 |
+
border-color: #667eea;
|
| 212 |
+
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
|
| 213 |
+
background: white;
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
/* Upload Area */
|
| 217 |
+
.upload-area {
|
| 218 |
+
border: 3px dashed rgba(102, 126, 234, 0.3);
|
| 219 |
+
border-radius: 20px;
|
| 220 |
+
padding: 4rem 2rem;
|
| 221 |
+
text-align: center;
|
| 222 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
| 223 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 224 |
+
cursor: pointer;
|
| 225 |
+
position: relative;
|
| 226 |
+
overflow: hidden;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.upload-area::before {
|
| 230 |
+
content: '';
|
| 231 |
+
position: absolute;
|
| 232 |
+
top: -50%;
|
| 233 |
+
left: -50%;
|
| 234 |
+
width: 200%;
|
| 235 |
+
height: 200%;
|
| 236 |
+
background: radial-gradient(circle, rgba(102, 126, 234, 0.1) 0%, transparent 70%);
|
| 237 |
+
opacity: 0;
|
| 238 |
+
transition: opacity 0.3s;
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
.upload-area:hover::before {
|
| 242 |
+
opacity: 1;
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
.upload-area:hover {
|
| 246 |
+
border-color: #667eea;
|
| 247 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 248 |
+
transform: scale(1.02);
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
.upload-area.drag-active {
|
| 252 |
+
border-color: #764ba2;
|
| 253 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%);
|
| 254 |
+
transform: scale(1.05);
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
/* Progress Bar */
|
| 258 |
+
.progress-bar {
|
| 259 |
+
width: 100%;
|
| 260 |
+
height: 10px;
|
| 261 |
+
background: rgba(102, 126, 234, 0.1);
|
| 262 |
+
border-radius: 10px;
|
| 263 |
+
overflow: hidden;
|
| 264 |
+
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.progress-fill {
|
| 268 |
+
height: 100%;
|
| 269 |
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
|
| 270 |
+
background-size: 200% 100%;
|
| 271 |
+
animation: progressShine 2s linear infinite;
|
| 272 |
+
transition: width 0.3s ease;
|
| 273 |
+
border-radius: 10px;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
@keyframes progressShine {
|
| 277 |
+
0% {
|
| 278 |
+
background-position: 200% 0;
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
100% {
|
| 282 |
+
background-position: -200% 0;
|
| 283 |
+
}
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
/* Loading Spinner */
|
| 287 |
+
.spinner {
|
| 288 |
+
width: 48px;
|
| 289 |
+
height: 48px;
|
| 290 |
+
border: 5px solid rgba(102, 126, 234, 0.1);
|
| 291 |
+
border-top-color: #667eea;
|
| 292 |
+
border-radius: 50%;
|
| 293 |
+
animation: spin 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
@keyframes spin {
|
| 297 |
+
to {
|
| 298 |
+
transform: rotate(360deg);
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
/* Alert Messages */
|
| 303 |
+
.alert {
|
| 304 |
+
padding: 1.25rem 1.75rem;
|
| 305 |
+
border-radius: 12px;
|
| 306 |
+
margin-bottom: 1.25rem;
|
| 307 |
+
border-left: 5px solid;
|
| 308 |
+
backdrop-filter: blur(10px);
|
| 309 |
+
font-weight: 500;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
.alert-success {
|
| 313 |
+
background: rgba(16, 185, 129, 0.1);
|
| 314 |
+
border-color: #10b981;
|
| 315 |
+
color: #065f46;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.alert-error {
|
| 319 |
+
background: rgba(239, 68, 68, 0.1);
|
| 320 |
+
border-color: #ef4444;
|
| 321 |
+
color: #991b1b;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.alert-info {
|
| 325 |
+
background: rgba(59, 130, 246, 0.1);
|
| 326 |
+
border-color: #3b82f6;
|
| 327 |
+
color: #1e40af;
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
/* Metric Cards */
|
| 331 |
+
.metric-card {
|
| 332 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 333 |
+
border-radius: 16px;
|
| 334 |
+
padding: 2rem;
|
| 335 |
+
border: 1px solid rgba(102, 126, 234, 0.2);
|
| 336 |
+
text-align: center;
|
| 337 |
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 338 |
+
position: relative;
|
| 339 |
+
overflow: hidden;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.metric-card::before {
|
| 343 |
+
content: '';
|
| 344 |
+
position: absolute;
|
| 345 |
+
top: -50%;
|
| 346 |
+
right: -50%;
|
| 347 |
+
width: 200%;
|
| 348 |
+
height: 200%;
|
| 349 |
+
background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%);
|
| 350 |
+
opacity: 0;
|
| 351 |
+
transition: opacity 0.3s;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
.metric-card:hover::before {
|
| 355 |
+
opacity: 1;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
.metric-card:hover {
|
| 359 |
+
transform: translateY(-8px) scale(1.05);
|
| 360 |
+
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.2);
|
| 361 |
+
border-color: #667eea;
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
.metric-value {
|
| 365 |
+
font-size: 2.5rem;
|
| 366 |
+
font-weight: 800;
|
| 367 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 368 |
+
-webkit-background-clip: text;
|
| 369 |
+
-webkit-text-fill-color: transparent;
|
| 370 |
+
margin-bottom: 0.5rem;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
.metric-label {
|
| 374 |
+
font-size: 0.95rem;
|
| 375 |
+
color: #6b7280;
|
| 376 |
+
font-weight: 600;
|
| 377 |
+
text-transform: uppercase;
|
| 378 |
+
letter-spacing: 0.5px;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
/* Utility Classes */
|
| 382 |
+
.flex {
|
| 383 |
+
display: flex;
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
.items-center {
|
| 387 |
+
align-items: center;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
.justify-between {
|
| 391 |
+
justify-content: space-between;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
.gap-2 {
|
| 395 |
+
gap: 0.5rem;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
.gap-3 {
|
| 399 |
+
gap: 0.75rem;
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
/* Grid Classes */
|
| 403 |
+
.grid {
|
| 404 |
+
display: grid;
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
.grid-cols-2 {
|
| 408 |
+
grid-template-columns: repeat(2, 1fr);
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
.grid-cols-4 {
|
| 412 |
+
grid-template-columns: repeat(4, 1fr);
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.gap-4 {
|
| 416 |
+
gap: 1rem;
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
/* Margin Classes */
|
| 420 |
+
.mb-4 {
|
| 421 |
+
margin-bottom: 1rem;
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
@media (max-width: 768px) {
|
| 425 |
+
|
| 426 |
+
.grid-cols-2,
|
| 427 |
+
.grid-cols-4 {
|
| 428 |
+
grid-template-columns: 1fr;
|
| 429 |
+
}
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
/* Scrollbar */
|
| 433 |
+
::-webkit-scrollbar {
|
| 434 |
+
width: 12px;
|
| 435 |
+
}
|
| 436 |
+
|
| 437 |
+
::-webkit-scrollbar-track {
|
| 438 |
+
background: rgba(255, 255, 255, 0.1);
|
| 439 |
+
border-radius: 10px;
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
::-webkit-scrollbar-thumb {
|
| 443 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 444 |
+
border-radius: 10px;
|
| 445 |
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
::-webkit-scrollbar-thumb:hover {
|
| 449 |
+
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
| 450 |
+
}
|
frontend/src/main.jsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react'
|
| 2 |
+
import ReactDOM from 'react-dom/client'
|
| 3 |
+
import App from './App.jsx'
|
| 4 |
+
import './index.css'
|
| 5 |
+
|
| 6 |
+
ReactDOM.createRoot(document.getElementById('root')).render(
|
| 7 |
+
<React.StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</React.StrictMode>,
|
| 10 |
+
)
|
frontend/vite.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'vite'
|
| 2 |
+
import react from '@vitejs/plugin-react'
|
| 3 |
+
|
| 4 |
+
export default defineConfig({
|
| 5 |
+
plugins: [react()],
|
| 6 |
+
server: {
|
| 7 |
+
port: 5173,
|
| 8 |
+
proxy: {
|
| 9 |
+
'/api': {
|
| 10 |
+
target: 'http://localhost:5000',
|
| 11 |
+
changeOrigin: true
|
| 12 |
+
}
|
| 13 |
+
}
|
| 14 |
+
}
|
| 15 |
+
})
|
sample_training_data.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Training ID,Date,Location,State,District,Theme,Participants,Trainer,Organization,Duration,Completion Rate,Feedback Score,Session Type
|
| 2 |
+
TR001,2024-01-15,Delhi,Delhi,Central Delhi,Earthquake,50,Dr. Rajesh Sharma,NIDM,2 days,95%,4.5,Workshop
|
| 3 |
+
TR002,2024-01-20,Mumbai,Maharashtra,Mumbai City,Flood,75,Mr. Amit Patel,SDMA Maharashtra,3 days,88%,4.3,Training Camp
|
| 4 |
+
TR003,2024-01-25,Kolkata,West Bengal,Kolkata,Cyclone,60,Ms. Priya Sen,NGO - Disaster Relief,2 days,92%,4.7,Awareness Program
|
| 5 |
+
TR004,2024-02-01,Chennai,Tamil Nadu,Chennai,Flood,80,Dr. Kumar Swamy,ATI Chennai,4 days,90%,4.6,Capacity Building
|
| 6 |
+
TR005,2024-02-05,Bangalore,Karnataka,Bangalore Urban,Fire Safety,45,Mr. Ravi Kumar,Fire Department,1 day,85%,4.2,Drill Exercise
|
| 7 |
+
TR006,2024-02-10,Hyderabad,Telangana,Hyderabad,Earthquake,55,Dr. Lakshmi Reddy,SDMA Telangana,3 days,93%,4.8,Workshop
|
| 8 |
+
TR007,2024-02-15,Ahmedabad,Gujarat,Ahmedabad,Earthquake,70,Mr. Vijay Shah,NDRF,2 days,89%,4.4,Training Camp
|
| 9 |
+
TR008,2024-02-20,Pune,Maharashtra,Pune,Landslide,40,Ms. Sneha Desai,Geological Survey,2 days,91%,4.5,Awareness Program
|
| 10 |
+
TR009,2024-02-25,Jaipur,Rajasthan,Jaipur,Drought,65,Dr. Anil Sharma,Agricultural Dept,3 days,87%,4.3,Capacity Building
|
| 11 |
+
TR010,2024-03-01,Lucknow,Uttar Pradesh,Lucknow,Flood,90,Mr. Suresh Yadav,SDMA UP,4 days,94%,4.7,Workshop
|
| 12 |
+
TR011,2024-03-05,Bhopal,Madhya Pradesh,Bhopal,Industrial Disaster,50,Dr. Meena Verma,Industrial Safety,2 days,88%,4.4,Training Camp
|
| 13 |
+
TR012,2024-03-10,Patna,Bihar,Patna,Flood,100,Mr. Rajesh Singh,NDRF Bihar,5 days,92%,4.6,Capacity Building
|
| 14 |
+
TR013,2024-03-15,Chandigarh,Punjab,Chandigarh,Earthquake,55,Ms. Harpreet Kaur,NIDM,2 days,90%,4.5,Workshop
|
| 15 |
+
TR014,2024-03-20,Thiruvananthapuram,Kerala,Thiruvananthapuram,Flood,85,Dr. Suresh Nair,SDMA Kerala,3 days,95%,4.9,Training Camp
|
| 16 |
+
TR015,2024-03-25,Guwahati,Assam,Kamrup Metropolitan,Flood,70,Mr. Bikash Das,NDRF Assam,4 days,89%,4.3,Awareness Program
|
| 17 |
+
TR016,2024-04-01,Ranchi,Jharkhand,Ranchi,Forest Fire,45,Ms. Anita Kumari,Forest Department,2 days,86%,4.2,Drill Exercise
|
| 18 |
+
TR017,2024-04-05,Shimla,Himachal Pradesh,Shimla,Landslide,35,Dr. Vikram Singh,Geological Survey,2 days,91%,4.6,Workshop
|
| 19 |
+
TR018,2024-04-10,Dehradun,Uttarakhand,Dehradun,Earthquake,60,Mr. Praveen Rawat,SDMA Uttarakhand,3 days,93%,4.7,Training Camp
|
| 20 |
+
TR019,2024-04-15,Srinagar,Jammu and Kashmir,Srinagar,Avalanche,40,Ms. Zainab Khan,NDRF J&K,2 days,88%,4.4,Awareness Program
|
| 21 |
+
TR020,2024-04-20,Bhubaneswar,Odisha,Khordha,Cyclone,95,Dr. Prasad Rao,SDMA Odisha,4 days,96%,4.8,Capacity Building
|
| 22 |
+
TR021,2024-04-25,Raipur,Chhattisgarh,Raipur,Drought,50,Mr. Ramesh Patel,Agricultural Dept,2 days,87%,4.3,Workshop
|
| 23 |
+
TR022,2024-05-01,Gandhinagar,Gujarat,Gandhinagar,Industrial Disaster,55,Dr. Kiran Mehta,Industrial Safety,3 days,90%,4.5,Training Camp
|
| 24 |
+
TR023,2024-05-05,Imphal,Manipur,Imphal West,Earthquake,30,Ms. Chanu Devi,SDMA Manipur,2 days,85%,4.2,Awareness Program
|
| 25 |
+
TR024,2024-05-10,Agartala,Tripura,West Tripura,Flood,45,Mr. Debashish Roy,NDRF Tripura,3 days,89%,4.4,Capacity Building
|
| 26 |
+
TR025,2024-05-15,Kohima,Nagaland,Kohima,Landslide,25,Dr. Neiphiu Rio,Geological Survey,2 days,84%,4.1,Workshop
|
| 27 |
+
TR026,2024-05-20,Aizawl,Mizoram,Aizawl,Earthquake,20,Ms. Lalrinsangi,SDMA Mizoram,2 days,82%,4.0,Training Camp
|
| 28 |
+
TR027,2024-05-25,Shillong,Meghalaya,East Khasi Hills,Flood,40,Mr. Kong Syiem,NDRF Meghalaya,3 days,88%,4.3,Awareness Program
|
| 29 |
+
TR028,2024-06-01,Itanagar,Arunachal Pradesh,Papum Pare,Landslide,30,Dr. Taba Tedir,Geological Survey,2 days,86%,4.2,Capacity Building
|
| 30 |
+
TR029,2024-06-05,Gangtok,Sikkim,East Sikkim,Earthquake,35,Ms. Yangchen Lepcha,SDMA Sikkim,3 days,91%,4.6,Workshop
|
| 31 |
+
TR030,2024-06-10,Port Blair,Andaman and Nicobar,South Andaman,Tsunami,50,Mr. Rajiv Kumar,NDRF A&N,4 days,93%,4.7,Training Camp
|
| 32 |
+
TR031,2024-06-15,Panaji,Goa,North Goa,Flood,40,Dr. Maria Fernandes,SDMA Goa,2 days,89%,4.4,Awareness Program
|
| 33 |
+
TR032,2024-06-20,Daman,Daman and Diu,Daman,Cyclone,25,Mr. Patel Ramesh,NDRF Daman,2 days,87%,4.3,Drill Exercise
|
| 34 |
+
TR033,2024-06-25,Puducherry,Puducherry,Puducherry,Flood,55,Ms. Lakshmi Iyer,SDMA Puducherry,3 days,92%,4.6,Capacity Building
|
| 35 |
+
TR034,2024-07-01,Leh,Ladakh,Leh,Avalanche,30,Dr. Tsering Dorje,NDRF Ladakh,2 days,88%,4.4,Workshop
|
| 36 |
+
TR035,2024-07-05,Jammu,Jammu and Kashmir,Jammu,Earthquake,65,Mr. Ashok Sharma,SDMA J&K,3 days,90%,4.5,Training Camp
|
| 37 |
+
TR036,2024-07-10,Varanasi,Uttar Pradesh,Varanasi,Flood,75,Dr. Ram Prakash,NDRF UP,4 days,94%,4.7,Awareness Program
|
| 38 |
+
TR037,2024-07-15,Indore,Madhya Pradesh,Indore,Fire Safety,50,Ms. Rekha Jain,Fire Department,2 days,86%,4.2,Drill Exercise
|
| 39 |
+
TR038,2024-07-20,Nagpur,Maharashtra,Nagpur,Drought,60,Mr. Sunil Deshmukh,Agricultural Dept,3 days,88%,4.3,Capacity Building
|
| 40 |
+
TR039,2024-07-25,Visakhapatnam,Andhra Pradesh,Visakhapatnam,Cyclone,80,Dr. Venkat Rao,SDMA AP,4 days,95%,4.8,Workshop
|
| 41 |
+
TR040,2024-08-01,Vijayawada,Andhra Pradesh,Krishna,Flood,70,Ms. Padma Lakshmi,NDRF AP,3 days,91%,4.6,Training Camp
|
| 42 |
+
TR041,2024-08-05,Coimbatore,Tamil Nadu,Coimbatore,Fire Safety,45,Mr. Murugan Raj,Fire Department,2 days,87%,4.3,Awareness Program
|
| 43 |
+
TR042,2024-08-10,Madurai,Tamil Nadu,Madurai,Flood,65,Dr. Selvam Kumar,SDMA TN,3 days,92%,4.7,Capacity Building
|
| 44 |
+
TR043,2024-08-15,Mysore,Karnataka,Mysore,Earthquake,55,Ms. Kavitha Reddy,NIDM,2 days,89%,4.4,Workshop
|
| 45 |
+
TR044,2024-08-20,Mangalore,Karnataka,Dakshina Kannada,Landslide,40,Mr. Suresh Shetty,Geological Survey,2 days,90%,4.5,Training Camp
|
| 46 |
+
TR045,2024-08-25,Kochi,Kerala,Ernakulam,Flood,90,Dr. Thomas Joseph,SDMA Kerala,4 days,96%,4.9,Awareness Program
|
| 47 |
+
TR046,2024-09-01,Kozhikode,Kerala,Kozhikode,Landslide,50,Ms. Ayesha Beevi,NDRF Kerala,3 days,93%,4.7,Capacity Building
|
| 48 |
+
TR047,2024-09-05,Jodhpur,Rajasthan,Jodhpur,Drought,55,Mr. Mahendra Singh,Agricultural Dept,2 days,85%,4.2,Workshop
|
| 49 |
+
TR048,2024-09-10,Udaipur,Rajasthan,Udaipur,Earthquake,45,Dr. Geeta Sharma,SDMA Rajasthan,3 days,88%,4.4,Training Camp
|
| 50 |
+
TR049,2024-09-15,Amritsar,Punjab,Amritsar,Flood,60,Mr. Gurpreet Singh,NDRF Punjab,3 days,91%,4.6,Awareness Program
|
| 51 |
+
TR050,2024-09-20,Ludhiana,Punjab,Ludhiana,Industrial Disaster,50,Ms. Simran Kaur,Industrial Safety,2 days,89%,4.5,Drill Exercise
|
test_data_1_recent.csv
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Training ID,Date,Location,State,District,Theme,Participants,Trainer,Organization,Duration,Completion Rate,Feedback Score,Session Type
|
| 2 |
+
TR101,2024-10-01,New Delhi,Delhi,Central Delhi,Earthquake,120,Dr. Arun Sharma,NIDM,3 days,98%,4.8,Capacity Building
|
| 3 |
+
TR102,2024-10-03,Mumbai,Maharashtra,Mumbai City,Flood,150,Ms. Priya Desai,SDMA Maharashtra,4 days,95%,4.7,Training Camp
|
| 4 |
+
TR103,2024-10-05,Kolkata,West Bengal,Kolkata,Cyclone,85,Mr. Rajesh Kumar,NGO - Disaster Relief,2 days,92%,4.6,Awareness Program
|
| 5 |
+
TR104,2024-10-07,Chennai,Tamil Nadu,Chennai,Tsunami,100,Dr. Venkat Rao,ATI Chennai,3 days,96%,4.9,Workshop
|
| 6 |
+
TR105,2024-10-09,Bangalore,Karnataka,Bangalore Urban,Fire Safety,65,Mr. Suresh Reddy,Fire Department,1 day,88%,4.3,Drill Exercise
|
| 7 |
+
TR106,2024-10-11,Hyderabad,Telangana,Hyderabad,Earthquake,90,Dr. Lakshmi Devi,SDMA Telangana,2 days,94%,4.7,Training Camp
|
| 8 |
+
TR107,2024-10-13,Ahmedabad,Gujarat,Ahmedabad,Industrial Disaster,75,Mr. Vijay Patel,Industrial Safety,3 days,91%,4.5,Capacity Building
|
| 9 |
+
TR108,2024-10-15,Pune,Maharashtra,Pune,Landslide,55,Ms. Anjali Shah,Geological Survey,2 days,89%,4.4,Workshop
|
| 10 |
+
TR109,2024-10-17,Jaipur,Rajasthan,Jaipur,Drought,80,Dr. Mahendra Singh,Agricultural Dept,3 days,90%,4.6,Awareness Program
|
| 11 |
+
TR110,2024-10-19,Lucknow,Uttar Pradesh,Lucknow,Flood,110,Mr. Ramesh Yadav,SDMA UP,4 days,97%,4.8,Training Camp
|
| 12 |
+
TR111,2024-10-21,Bhopal,Madhya Pradesh,Bhopal,Chemical Disaster,60,Dr. Kavita Verma,Industrial Safety,2 days,87%,4.2,Workshop
|
| 13 |
+
TR112,2024-10-23,Patna,Bihar,Patna,Flood,130,Mr. Anil Kumar,NDRF Bihar,5 days,95%,4.7,Capacity Building
|
| 14 |
+
TR113,2024-10-25,Chandigarh,Punjab,Chandigarh,Earthquake,70,Ms. Simran Kaur,NIDM,2 days,93%,4.6,Training Camp
|
| 15 |
+
TR114,2024-10-27,Thiruvananthapuram,Kerala,Thiruvananthapuram,Flood,95,Dr. Thomas Joseph,SDMA Kerala,3 days,98%,4.9,Awareness Program
|
| 16 |
+
TR115,2024-10-29,Guwahati,Assam,Kamrup Metropolitan,Flood,85,Mr. Bikash Das,NDRF Assam,4 days,91%,4.5,Workshop
|
| 17 |
+
TR116,2024-11-01,Ranchi,Jharkhand,Ranchi,Forest Fire,50,Ms. Priya Kumari,Forest Department,2 days,86%,4.3,Drill Exercise
|
| 18 |
+
TR117,2024-11-03,Shimla,Himachal Pradesh,Shimla,Avalanche,40,Dr. Vikram Singh,Geological Survey,2 days,90%,4.5,Training Camp
|
| 19 |
+
TR118,2024-11-05,Dehradun,Uttarakhand,Dehradun,Landslide,65,Mr. Praveen Rawat,SDMA Uttarakhand,3 days,94%,4.7,Capacity Building
|
| 20 |
+
TR119,2024-11-07,Srinagar,Jammu and Kashmir,Srinagar,Avalanche,45,Ms. Zainab Khan,NDRF J&K,2 days,89%,4.4,Workshop
|
| 21 |
+
TR120,2024-11-09,Bhubaneswar,Odisha,Khordha,Cyclone,140,Dr. Prasad Rao,SDMA Odisha,4 days,99%,4.9,Training Camp
|
| 22 |
+
TR121,2024-11-11,Raipur,Chhattisgarh,Raipur,Drought,60,Mr. Ramesh Patel,Agricultural Dept,2 days,88%,4.3,Awareness Program
|
| 23 |
+
TR122,2024-11-13,Gandhinagar,Gujarat,Gandhinagar,Earthquake,80,Dr. Kiran Mehta,NIDM,3 days,92%,4.6,Capacity Building
|
| 24 |
+
TR123,2024-11-15,Imphal,Manipur,Imphal West,Earthquake,35,Ms. Chanu Devi,SDMA Manipur,2 days,85%,4.2,Workshop
|
| 25 |
+
TR124,2024-11-17,Agartala,Tripura,West Tripura,Flood,50,Mr. Debashish Roy,NDRF Tripura,3 days,90%,4.5,Training Camp
|
| 26 |
+
TR125,2024-11-19,Kohima,Nagaland,Kohima,Landslide,30,Dr. Neiphiu Rio,Geological Survey,2 days,84%,4.1,Awareness Program
|
| 27 |
+
TR126,2024-11-21,Aizawl,Mizoram,Aizawl,Earthquake,25,Ms. Lalrinsangi,SDMA Mizoram,2 days,82%,4.0,Workshop
|
| 28 |
+
TR127,2024-11-23,Shillong,Meghalaya,East Khasi Hills,Flood,45,Mr. Kong Syiem,NDRF Meghalaya,3 days,89%,4.4,Training Camp
|
| 29 |
+
TR128,2024-11-25,Itanagar,Arunachal Pradesh,Papum Pare,Landslide,35,Dr. Taba Tedir,Geological Survey,2 days,87%,4.3,Capacity Building
|
| 30 |
+
TR129,2024-11-27,Gangtok,Sikkim,East Sikkim,Earthquake,40,Ms. Yangchen Lepcha,SDMA Sikkim,3 days,92%,4.6,Workshop
|
| 31 |
+
TR130,2024-11-29,Port Blair,Andaman and Nicobar,South Andaman,Tsunami,55,Mr. Rajiv Kumar,NDRF A&N,4 days,95%,4.8,Training Camp
|
| 32 |
+
TR131,2024-12-01,Panaji,Goa,North Goa,Flood,50,Dr. Maria Fernandes,SDMA Goa,2 days,91%,4.5,Awareness Program
|
| 33 |
+
TR132,2024-12-03,Varanasi,Uttar Pradesh,Varanasi,Flood,90,Dr. Ram Prakash,NDRF UP,4 days,96%,4.8,Capacity Building
|
| 34 |
+
TR133,2024-12-05,Indore,Madhya Pradesh,Indore,Fire Safety,55,Ms. Rekha Jain,Fire Department,2 days,87%,4.3,Drill Exercise
|
| 35 |
+
TR134,2024-12-07,Nagpur,Maharashtra,Nagpur,Drought,70,Mr. Sunil Deshmukh,Agricultural Dept,3 days,89%,4.4,Workshop
|
| 36 |
+
TR135,2024-12-09,Visakhapatnam,Andhra Pradesh,Visakhapatnam,Cyclone,105,Dr. Venkat Rao,SDMA AP,4 days,97%,4.9,Training Camp
|
test_data_2_detailed.csv
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Training ID,Date,Location,State,District,Theme,Participants,Trainer,Organization,Duration,Completion Rate,Feedback Score,Session Type,Budget (INR),Target Audience,Language,Follow-up Required,Certification Provided
|
| 2 |
+
TR201,2024-01-10,Delhi,Delhi,New Delhi,Earthquake Preparedness,200,Dr. Rajesh Kumar,NIDM,5 days,97%,4.8,Intensive Workshop,500000,Government Officials,Hindi & English,Yes,Yes
|
| 3 |
+
TR202,2024-01-15,Mumbai,Maharashtra,Mumbai Suburban,Flood Management,180,Ms. Sneha Patel,SDMA Maharashtra,4 days,94%,4.6,Training of Trainers,450000,NGO Workers,Marathi & English,Yes,Yes
|
| 4 |
+
TR203,2024-01-20,Kolkata,West Bengal,Kolkata,Cyclone Response,150,Mr. Amit Ghosh,NDRF,3 days,96%,4.7,Field Exercise,400000,Emergency Responders,Bengali & English,No,Yes
|
| 5 |
+
TR204,2024-02-01,Chennai,Tamil Nadu,Chennai,Tsunami Early Warning,120,Dr. Lakshmi Iyer,ATI Chennai,4 days,98%,4.9,Technical Training,550000,Technical Staff,Tamil & English,Yes,Yes
|
| 6 |
+
TR205,2024-02-10,Bangalore,Karnataka,Bangalore Urban,Urban Fire Safety,90,Mr. Karthik Reddy,Fire Services,2 days,89%,4.4,Awareness Campaign,200000,Building Managers,Kannada & English,No,No
|
| 7 |
+
TR206,2024-02-18,Hyderabad,Telangana,Hyderabad,Industrial Hazard,110,Dr. Madhavi Rao,Industrial Safety Board,3 days,92%,4.5,Specialized Training,480000,Factory Workers,Telugu & English,Yes,Yes
|
| 8 |
+
TR207,2024-02-25,Ahmedabad,Gujarat,Ahmedabad,Earthquake Retrofitting,85,Mr. Jayesh Shah,Geological Survey,4 days,95%,4.7,Technical Workshop,520000,Engineers,Gujarati & English,Yes,Yes
|
| 9 |
+
TR208,2024-03-05,Pune,Maharashtra,Pune,Landslide Mitigation,75,Ms. Pooja Deshmukh,Geological Survey,3 days,91%,4.6,Field Training,380000,Local Authorities,Marathi & English,No,Yes
|
| 10 |
+
TR209,2024-03-12,Jaipur,Rajasthan,Jaipur,Drought Management,160,Dr. Mahendra Singh,Agricultural Dept,5 days,93%,4.5,Capacity Building,450000,Farmers & Officials,Hindi,Yes,No
|
| 11 |
+
TR210,2024-03-20,Lucknow,Uttar Pradesh,Lucknow,Flood Rescue Operations,140,Mr. Vikram Yadav,NDRF UP,4 days,96%,4.8,Practical Training,500000,Rescue Teams,Hindi,No,Yes
|
| 12 |
+
TR211,2024-03-28,Bhopal,Madhya Pradesh,Bhopal,Chemical Emergency,95,Dr. Anjali Verma,Industrial Safety,3 days,88%,4.3,Specialized Training,420000,Industrial Workers,Hindi,Yes,Yes
|
| 13 |
+
TR212,2024-04-05,Patna,Bihar,Patna,Flood Early Warning,170,Mr. Rajesh Singh,SDMA Bihar,5 days,94%,4.7,Community Training,380000,Village Leaders,Hindi,Yes,No
|
| 14 |
+
TR213,2024-04-15,Chandigarh,Punjab,Chandigarh,Earthquake Safety,100,Ms. Harpreet Kaur,NIDM,3 days,92%,4.6,Workshop,350000,School Teachers,Punjabi & Hindi,No,Yes
|
| 15 |
+
TR214,2024-04-22,Thiruvananthapuram,Kerala,Thiruvananthapuram,Monsoon Preparedness,130,Dr. Suresh Nair,SDMA Kerala,4 days,99%,4.9,Advanced Training,480000,District Officials,Malayalam & English,Yes,Yes
|
| 16 |
+
TR215,2024-05-01,Guwahati,Assam,Kamrup Metropolitan,Flood & Erosion,115,Mr. Bikash Das,NDRF Assam,5 days,90%,4.5,Field Training,420000,Riverine Communities,Assamese & English,Yes,No
|
| 17 |
+
TR216,2024-05-10,Ranchi,Jharkhand,Ranchi,Forest Fire Prevention,80,Ms. Priya Kumari,Forest Dept,3 days,87%,4.4,Awareness Program,280000,Forest Guards,Hindi,No,No
|
| 18 |
+
TR217,2024-05-18,Shimla,Himachal Pradesh,Shimla,Snow Avalanche,60,Dr. Vikram Singh,Geological Survey,4 days,91%,4.6,Specialized Training,450000,Mountain Rescue,Hindi & English,Yes,Yes
|
| 19 |
+
TR218,2024-05-25,Dehradun,Uttarakhand,Dehradun,Landslide Warning,95,Mr. Praveen Rawat,SDMA Uttarakhand,3 days,93%,4.7,Technical Workshop,400000,Engineers,Hindi,No,Yes
|
| 20 |
+
TR219,2024-06-02,Srinagar,Jammu and Kashmir,Srinagar,Avalanche Rescue,70,Ms. Zainab Khan,NDRF J&K,4 days,89%,4.4,Practical Training,480000,Rescue Teams,Urdu & English,Yes,Yes
|
| 21 |
+
TR220,2024-06-10,Bhubaneswar,Odisha,Khordha,Cyclone Shelter Management,185,Dr. Prasad Rao,SDMA Odisha,5 days,98%,4.9,Comprehensive Training,550000,Shelter Managers,Odia & English,Yes,Yes
|
| 22 |
+
TR221,2024-06-18,Raipur,Chhattisgarh,Raipur,Drought Resilience,90,Mr. Ramesh Patel,Agricultural Dept,3 days,88%,4.3,Community Workshop,320000,Farmers,Hindi,No,No
|
| 23 |
+
TR222,2024-06-25,Gandhinagar,Gujarat,Gandhinagar,Seismic Safety,105,Dr. Kiran Mehta,NIDM,4 days,94%,4.7,Technical Training,480000,Architects,Gujarati & English,Yes,Yes
|
| 24 |
+
TR223,2024-07-05,Imphal,Manipur,Imphal West,Earthquake Preparedness,55,Ms. Chanu Devi,SDMA Manipur,3 days,86%,4.2,Basic Training,280000,Local Communities,Manipuri & English,No,No
|
| 25 |
+
TR224,2024-07-12,Agartala,Tripura,West Tripura,Flood Response,75,Mr. Debashish Roy,NDRF Tripura,4 days,91%,4.5,Field Exercise,350000,Emergency Teams,Bengali & English,Yes,Yes
|
| 26 |
+
TR225,2024-07-20,Kohima,Nagaland,Kohima,Landslide Awareness,50,Dr. Neiphiu Rio,Geological Survey,2 days,85%,4.1,Awareness Campaign,220000,Village Communities,English,No,No
|
| 27 |
+
TR226,2024-07-28,Aizawl,Mizoram,Aizawl,Earthquake Safety,45,Ms. Lalrinsangi,SDMA Mizoram,3 days,83%,4.0,Basic Workshop,250000,Government Staff,Mizo & English,Yes,No
|
| 28 |
+
TR227,2024-08-05,Shillong,Meghalaya,East Khasi Hills,Flood Management,85,Mr. Kong Syiem,NDRF Meghalaya,4 days,90%,4.5,Practical Training,380000,Rescue Personnel,Khasi & English,No,Yes
|
| 29 |
+
TR228,2024-08-15,Itanagar,Arunachal Pradesh,Papum Pare,Landslide Mitigation,65,Dr. Taba Tedir,Geological Survey,3 days,88%,4.4,Technical Workshop,350000,Engineers,English,Yes,Yes
|
| 30 |
+
TR229,2024-08-22,Gangtok,Sikkim,East Sikkim,Earthquake Response,70,Ms. Yangchen Lepcha,SDMA Sikkim,4 days,93%,4.7,Comprehensive Training,420000,District Teams,Nepali & English,Yes,Yes
|
| 31 |
+
TR230,2024-08-30,Port Blair,Andaman and Nicobar,South Andaman,Tsunami Preparedness,80,Mr. Rajiv Kumar,NDRF A&N,5 days,96%,4.8,Island Safety Training,500000,Coastal Communities,English & Hindi,Yes,Yes
|
| 32 |
+
TR231,2024-09-08,Panaji,Goa,North Goa,Monsoon Safety,75,Dr. Maria Fernandes,SDMA Goa,3 days,92%,4.6,Community Training,320000,Tourism Sector,Konkani & English,No,Yes
|
| 33 |
+
TR232,2024-09-15,Varanasi,Uttar Pradesh,Varanasi,River Flood Management,125,Dr. Ram Prakash,NDRF UP,5 days,95%,4.8,Advanced Training,480000,Riverine Districts,Hindi,Yes,Yes
|
| 34 |
+
TR233,2024-09-22,Indore,Madhya Pradesh,Indore,Urban Fire Safety,85,Ms. Rekha Jain,Fire Department,2 days,88%,4.4,Drill Exercise,280000,Commercial Buildings,Hindi,No,No
|
| 35 |
+
TR234,2024-09-28,Nagpur,Maharashtra,Nagpur,Agricultural Drought,100,Mr. Sunil Deshmukh,Agricultural Dept,4 days,90%,4.5,Farmer Training,350000,Agricultural Communities,Marathi,Yes,No
|
| 36 |
+
TR235,2024-10-05,Visakhapatnam,Andhra Pradesh,Visakhapatnam,Cyclone Preparedness,155,Dr. Venkat Rao,SDMA AP,5 days,97%,4.9,Comprehensive Workshop,550000,Coastal Districts,Telugu & English,Yes,Yes
|
| 37 |
+
TR236,2024-10-12,Vijayawada,Andhra Pradesh,Krishna,Flood Control,135,Ms. Padma Lakshmi,NDRF AP,4 days,94%,4.7,Field Training,450000,River Communities,Telugu,No,Yes
|
| 38 |
+
TR237,2024-10-20,Coimbatore,Tamil Nadu,Coimbatore,Industrial Safety,90,Mr. Murugan Raj,Fire Services,3 days,89%,4.4,Technical Training,380000,Factory Workers,Tamil,Yes,Yes
|
| 39 |
+
TR238,2024-10-28,Madurai,Tamil Nadu,Madurai,Flood Response,110,Dr. Selvam Kumar,SDMA TN,4 days,93%,4.6,Capacity Building,420000,District Officials,Tamil,Yes,Yes
|
| 40 |
+
TR239,2024-11-05,Mysore,Karnataka,Mysore,Earthquake Awareness,95,Ms. Kavitha Reddy,NIDM,3 days,91%,4.5,Public Awareness,320000,General Public,Kannada,No,No
|
| 41 |
+
TR240,2024-11-12,Mangalore,Karnataka,Dakshina Kannada,Coastal Erosion,75,Mr. Suresh Shetty,Geological Survey,4 days,92%,4.6,Technical Workshop,400000,Coastal Engineers,Kannada & English,Yes,Yes
|
| 42 |
+
TR241,2024-11-20,Kochi,Kerala,Ernakulam,Monsoon Flooding,140,Dr. Thomas Joseph,SDMA Kerala,5 days,98%,4.9,Advanced Training,520000,Urban Planners,Malayalam & English,Yes,Yes
|
| 43 |
+
TR242,2024-11-28,Kozhikode,Kerala,Kozhikode,Landslide Prevention,85,Ms. Ayesha Beevi,NDRF Kerala,3 days,94%,4.7,Field Training,380000,Hill Communities,Malayalam,No,Yes
|
| 44 |
+
TR243,2024-12-05,Jodhpur,Rajasthan,Jodhpur,Desert Drought,95,Mr. Mahendra Singh,Agricultural Dept,4 days,87%,4.3,Community Workshop,340000,Desert Communities,Hindi,Yes,No
|
| 45 |
+
TR244,2024-12-10,Udaipur,Rajasthan,Udaipur,Water Conservation,80,Dr. Geeta Sharma,SDMA Rajasthan,3 days,90%,4.5,Awareness Program,300000,Rural Communities,Hindi,No,No
|
| 46 |
+
TR245,2024-12-15,Amritsar,Punjab,Amritsar,Flood Preparedness,105,Mr. Gurpreet Singh,NDRF Punjab,4 days,93%,4.7,Comprehensive Training,420000,Border Districts,Punjabi,Yes,Yes
|