cryogenic22 commited on
Commit
f2b2374
·
verified ·
1 Parent(s): e97c5c0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +159 -163
app.py CHANGED
@@ -1,131 +1,131 @@
1
- from fastapi import FastAPI, UploadFile, File, Request
2
  from fastapi.responses import HTMLResponse, JSONResponse
 
3
  from pydantic import BaseModel
4
  import pandas as pd
5
- from sentence_transformers import SentenceTransformer
6
  import numpy as np
7
- from typing import List, Dict
8
- import json
9
- import os
10
 
11
  app = FastAPI()
12
- model = SentenceTransformer('all-MiniLM-L6-v2')
13
 
14
- class QueryRequest(BaseModel):
15
- query: str
16
- embeddings: List[List[float]]
 
 
 
17
 
18
- @app.get("/", response_class=HTMLResponse)
19
- async def root():
20
- return """
21
- <!DOCTYPE html>
22
- <html>
23
- <head>
24
- <title>Analytics Dashboard</title>
25
- <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
26
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
27
- <script src="https://unpkg.com/recharts@2.1.9/umd/Recharts.js"></script>
28
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
29
- <script src="https://cdn.tailwindcss.com"></script>
30
- </head>
31
- <body>
32
- <div id="root"></div>
33
- <script type="text/babel">
34
- function App() {
35
- const [data, setData] = React.useState(null);
36
- const [query, setQuery] = React.useState('');
37
- const [insights, setInsights] = React.useState([]);
38
- const [loading, setLoading] = React.useState(false);
39
- const [error, setError] = React.useState(null);
40
-
41
- const handleFileUpload = async (event) => {
42
- const file = event.target.files[0];
43
- if (!file) return;
44
-
45
- setLoading(true);
46
- setError(null);
47
- const formData = new FormData();
48
- formData.append('file', file);
49
-
50
- try {
51
- const response = await fetch('/api/process-file', {
52
- method: 'POST',
53
- body: formData,
54
- });
55
-
56
- if (!response.ok) {
57
- throw new Error('Upload failed');
58
- }
59
-
60
- const result = await response.json();
61
- console.log('Upload result:', result);
62
- setData(result);
63
- } catch (err) {
64
- console.error('Upload error:', err);
65
- setError('Failed to upload file');
66
- } finally {
67
- setLoading(false);
68
- }
69
- };
70
-
71
- const handleQuery = async () => {
72
- if (!data || !query) return;
73
- setLoading(true);
74
- setError(null);
75
-
76
- try {
77
- const response = await fetch('/api/query', {
78
- method: 'POST',
79
- headers: {
80
- 'Content-Type': 'application/json',
81
- },
82
- body: JSON.stringify({
83
- query: query,
84
- embeddings: data.embeddings
85
- })
86
- });
87
-
88
- if (!response.ok) {
89
- throw new Error('Query failed');
90
- }
91
-
92
- const result = await response.json();
93
- console.log('Query result:', result);
94
- const newInsights = result.similar_indices.map(idx => data.raw_data[idx]);
95
- setInsights(newInsights);
96
- } catch (err) {
97
- console.error('Query error:', err);
98
- setError('Failed to process query');
99
- } finally {
100
- setLoading(false);
101
  }
102
- };
103
-
104
- return (
105
- <div className="p-6 max-w-6xl mx-auto">
106
- <h1 className="text-3xl font-bold mb-8">Data Analytics Dashboard</h1>
107
-
108
- {error && (
109
- <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
110
- {error}
111
- </div>
112
- )}
113
 
114
- <div className="mb-8">
115
- <label className="block text-sm font-medium mb-2">
116
- Upload Data File (.csv, .xlsx)
117
- </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  <input
119
  type="file"
120
- onChange={handleFileUpload}
121
- accept=".csv,.xlsx,.xls"
122
- className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
123
  disabled={loading}
124
  />
 
 
 
 
 
 
 
125
  </div>
126
 
127
  {data && (
128
- <div className="space-y-6">
129
  <div className="flex gap-2">
130
  <input
131
  type="text"
@@ -140,35 +140,20 @@ async def root():
140
  className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
141
  disabled={loading || !query}
142
  >
143
- {loading ? 'Processing...' : 'Search'}
144
  </button>
145
  </div>
146
 
147
  {insights.length > 0 && (
148
  <div className="mt-8">
149
  <h3 className="text-xl font-semibold mb-4">Related Insights</h3>
150
- <div className="h-96 mb-8">
151
- <Recharts.ResponsiveContainer width="100%" height="100%">
152
- <Recharts.BarChart data={insights}>
153
- {Object.keys(insights[0] || {})
154
- .filter(key => typeof insights[0][key] === 'number')
155
- .map((key) => (
156
- <Recharts.Bar key={key} dataKey={key} fill="#8884d8" />
157
- ))}
158
- <Recharts.XAxis />
159
- <Recharts.YAxis />
160
- <Recharts.Tooltip />
161
- <Recharts.Legend />
162
- </Recharts.BarChart>
163
- </Recharts.ResponsiveContainer>
164
- </div>
165
  <div className="grid gap-4">
166
  {insights.map((insight, idx) => (
167
  <div key={idx} className="p-4 bg-gray-50 rounded shadow">
168
  {Object.entries(insight).map(([key, value]) => (
169
  <div key={key} className="mb-1">
170
  <span className="font-medium">{key}:</span>{' '}
171
- {typeof value === 'number' ? value.toFixed(2) : value}
172
  </div>
173
  ))}
174
  </div>
@@ -179,58 +164,69 @@ async def root():
179
  </div>
180
  )}
181
  </div>
182
- );
183
- }
 
 
 
 
 
 
 
184
 
185
- ReactDOM.render(<App />, document.getElementById('root'));
186
- </script>
187
- </body>
188
- </html>
189
- """
 
 
190
 
191
  @app.post("/api/process-file")
192
  async def process_file(file: UploadFile = File(...)):
 
 
 
193
  try:
194
- content = await file.read()
195
- with open(f"temp_{file.filename}", "wb") as f:
196
- f.write(content)
197
-
198
- df = pd.read_excel(f"temp_{file.filename}") if file.filename.endswith('.xlsx') else pd.read_csv(f"temp_{file.filename}")
199
-
200
- text_reps = []
201
- for _, row in df.iterrows():
202
- text_rep = " ".join([f"{col}: {val}" for col, val in row.items()])
203
- text_reps.append(text_rep)
 
 
 
 
 
 
 
 
 
204
 
205
  embeddings = model.encode(text_reps)
206
- metadata = {
207
- 'columns': list(df.columns),
208
- 'row_count': len(df),
209
- 'numerical_cols': list(df.select_dtypes(include=[np.number]).columns),
210
- 'categorical_cols': list(df.select_dtypes(include=['object']).columns)
211
- }
212
 
213
- os.remove(f"temp_{file.filename}")
214
  return JSONResponse({
215
  'embeddings': embeddings.tolist(),
216
- 'metadata': metadata,
 
 
 
217
  'raw_data': df.to_dict('records')
218
  })
 
219
  except Exception as e:
220
- return JSONResponse(
221
- status_code=500,
222
- content={"error": str(e)}
223
- )
224
 
225
  @app.post("/api/query")
226
  async def query_data(request: QueryRequest):
227
  try:
228
  query_embedding = model.encode([request.query])[0]
229
  similarities = np.dot(request.embeddings, query_embedding)
230
- indices = np.argsort(similarities)[-5:][::-1].tolist()
231
- return JSONResponse({"similar_indices": indices})
232
  except Exception as e:
233
- return JSONResponse(
234
- status_code=500,
235
- content={"error": str(e)}
236
- )
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException
2
  from fastapi.responses import HTMLResponse, JSONResponse
3
+ from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
  import pandas as pd
6
+ import openpyxl
7
  import numpy as np
8
+ from sentence_transformers import SentenceTransformer
9
+ from typing import List
10
+ import io
11
 
12
  app = FastAPI()
 
13
 
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"],
17
+ allow_methods=["*"],
18
+ allow_headers=["*"]
19
+ )
20
 
21
+ model = SentenceTransformer('all-MiniLM-L6-v2')
22
+
23
+ HTML_CONTENT = """
24
+ <!DOCTYPE html>
25
+ <html>
26
+ <head>
27
+ <title>Analytics Dashboard</title>
28
+ <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
29
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
30
+ <script src="https://unpkg.com/recharts@2.1.9/umd/Recharts.js"></script>
31
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
32
+ <script src="https://cdn.tailwindcss.com"></script>
33
+ </head>
34
+ <body>
35
+ <div id="root"></div>
36
+ <script type="text/babel">
37
+ function App() {
38
+ const [file, setFile] = React.useState(null);
39
+ const [data, setData] = React.useState(null);
40
+ const [query, setQuery] = React.useState('');
41
+ const [insights, setInsights] = React.useState([]);
42
+ const [loading, setLoading] = React.useState(false);
43
+ const [error, setError] = React.useState(null);
44
+
45
+ const handleFileChange = (event) => {
46
+ setFile(event.target.files[0]);
47
+ setError(null);
48
+ };
49
+
50
+ const handleUpload = async () => {
51
+ if (!file) return;
52
+ setLoading(true);
53
+ setError(null);
54
+
55
+ const formData = new FormData();
56
+ formData.append('file', file);
57
+
58
+ try {
59
+ const response = await fetch('/api/process-file', {
60
+ method: 'POST',
61
+ body: formData,
62
+ });
63
+
64
+ if (!response.ok) {
65
+ const error = await response.json();
66
+ throw new Error(error.detail || 'Upload failed');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ const result = await response.json();
70
+ setData(result);
71
+ } catch (err) {
72
+ setError(err.message);
73
+ } finally {
74
+ setLoading(false);
75
+ }
76
+ };
77
+
78
+ const handleQuery = async () => {
79
+ if (!data || !query) return;
80
+ setLoading(true);
81
+ try {
82
+ const response = await fetch('/api/query', {
83
+ method: 'POST',
84
+ headers: {'Content-Type': 'application/json'},
85
+ body: JSON.stringify({query, embeddings: data.embeddings})
86
+ });
87
+
88
+ if (!response.ok) throw new Error('Query failed');
89
+
90
+ const result = await response.json();
91
+ setInsights(result.similar_indices.map(idx => data.raw_data[idx]));
92
+ } catch (err) {
93
+ setError(err.message);
94
+ } finally {
95
+ setLoading(false);
96
+ }
97
+ };
98
+
99
+ return (
100
+ <div className="p-6 max-w-6xl mx-auto">
101
+ <h1 className="text-3xl font-bold mb-8">Data Analytics Dashboard</h1>
102
+
103
+ {error && (
104
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
105
+ {error}
106
+ </div>
107
+ )}
108
+
109
+ <div className="space-y-4">
110
+ <div className="flex flex-col gap-2">
111
  <input
112
  type="file"
113
+ onChange={handleFileChange}
114
+ accept=".xlsx,.csv"
115
+ className="block w-full text-sm border rounded p-2"
116
  disabled={loading}
117
  />
118
+ <button
119
+ onClick={handleUpload}
120
+ disabled={!file || loading}
121
+ className="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50"
122
+ >
123
+ {loading ? 'Processing...' : 'Upload'}
124
+ </button>
125
  </div>
126
 
127
  {data && (
128
+ <div className="space-y-4">
129
  <div className="flex gap-2">
130
  <input
131
  type="text"
 
140
  className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
141
  disabled={loading || !query}
142
  >
143
+ Search
144
  </button>
145
  </div>
146
 
147
  {insights.length > 0 && (
148
  <div className="mt-8">
149
  <h3 className="text-xl font-semibold mb-4">Related Insights</h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  <div className="grid gap-4">
151
  {insights.map((insight, idx) => (
152
  <div key={idx} className="p-4 bg-gray-50 rounded shadow">
153
  {Object.entries(insight).map(([key, value]) => (
154
  <div key={key} className="mb-1">
155
  <span className="font-medium">{key}:</span>{' '}
156
+ {value}
157
  </div>
158
  ))}
159
  </div>
 
164
  </div>
165
  )}
166
  </div>
167
+ </div>
168
+ );
169
+ }
170
+
171
+ ReactDOM.render(<App />, document.getElementById('root'));
172
+ </script>
173
+ </body>
174
+ </html>
175
+ """
176
 
177
+ class QueryRequest(BaseModel):
178
+ query: str
179
+ embeddings: List[List[float]]
180
+
181
+ @app.get("/")
182
+ async def root():
183
+ return HTMLResponse(HTML_CONTENT)
184
 
185
  @app.post("/api/process-file")
186
  async def process_file(file: UploadFile = File(...)):
187
+ if not file.filename:
188
+ raise HTTPException(status_code=400, detail="No file provided")
189
+
190
  try:
191
+ contents = await file.read()
192
+ if file.filename.endswith('.xlsx'):
193
+ wb = openpyxl.load_workbook(io.BytesIO(contents), read_only=True)
194
+ sheet = wb.active
195
+ data = []
196
+ headers = next(sheet.rows)
197
+ header_values = [cell.value for cell in headers]
198
+ for row in sheet.rows:
199
+ if row != headers:
200
+ data.append({h: cell.value for h, cell in zip(header_values, row)})
201
+ df = pd.DataFrame(data)
202
+ elif file.filename.endswith('.csv'):
203
+ df = pd.read_csv(io.BytesIO(contents))
204
+ else:
205
+ raise HTTPException(status_code=400, detail="Unsupported file type")
206
+
207
+ df = df.replace({np.nan: None})
208
+ text_reps = [" ".join(f"{col}: {str(val)}" for col, val in row.items() if val is not None)
209
+ for _, row in df.iterrows()]
210
 
211
  embeddings = model.encode(text_reps)
 
 
 
 
 
 
212
 
 
213
  return JSONResponse({
214
  'embeddings': embeddings.tolist(),
215
+ 'metadata': {
216
+ 'columns': list(df.columns),
217
+ 'row_count': len(df)
218
+ },
219
  'raw_data': df.to_dict('records')
220
  })
221
+
222
  except Exception as e:
223
+ raise HTTPException(status_code=500, detail=str(e))
 
 
 
224
 
225
  @app.post("/api/query")
226
  async def query_data(request: QueryRequest):
227
  try:
228
  query_embedding = model.encode([request.query])[0]
229
  similarities = np.dot(request.embeddings, query_embedding)
230
+ return JSONResponse({"similar_indices": np.argsort(similarities)[-5:][::-1].tolist()})
 
231
  except Exception as e:
232
+ raise HTTPException(status_code=500, detail=str(e))