Seth commited on
Commit
bc701fb
·
1 Parent(s): b667e4a
backend/app/main.py CHANGED
@@ -197,8 +197,61 @@ async def upload_asset(
197
  user_id=1 # Default user - in production, get from session/auth
198
  )
199
  db.add(db_asset)
200
- db.commit()
201
- db.refresh(db_asset)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  return {
204
  "id": db_asset.id,
@@ -207,7 +260,7 @@ async def upload_asset(
207
  "product_category": db_asset.product_category,
208
  "sub_category": db_asset.sub_category,
209
  "size": db_asset.size,
210
- "created_at": db_asset.created_at.isoformat()
211
  }
212
  except Exception as db_error:
213
  # If database save fails, still return success (file is saved)
@@ -226,20 +279,112 @@ async def upload_asset(
226
  raise HTTPException(status_code=500, detail=str(e))
227
 
228
  @app.get("/api/assets", response_model=List[AssetResponse])
229
- async def get_assets(product_category: Optional[str] = None):
 
 
 
230
  """Get list of assets"""
231
- # Mock data for now
232
- return [
233
- {
234
- "id": 1,
235
- "name": "OCR_Demo_Screenshot.png",
236
- "file_type": "image",
237
- "product_category": "ocr",
238
- "sub_category": None,
239
- "size": 2516582,
240
- "created_at": datetime.utcnow()
241
- }
242
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
  # ---- Post Management ----
245
 
 
197
  user_id=1 # Default user - in production, get from session/auth
198
  )
199
  db.add(db_asset)
200
+ try:
201
+ db.commit()
202
+ try:
203
+ db.refresh(db_asset)
204
+ except Exception as refresh_error:
205
+ # Refresh might fail due to version string, but commit succeeded
206
+ # Query the asset back to get the ID
207
+ if "Could not determine version" in str(refresh_error):
208
+ from sqlalchemy import text
209
+ result = db.execute(
210
+ text("SELECT id, created_at FROM assets WHERE name = :name AND file_path = :file_path ORDER BY id DESC LIMIT 1"),
211
+ {"name": file.filename, "file_path": str(file_path)}
212
+ )
213
+ row = result.fetchone()
214
+ if row:
215
+ db_asset.id = row[0]
216
+ # Set created_at if available
217
+ if hasattr(db_asset, 'created_at') and row[1]:
218
+ db_asset.created_at = row[1]
219
+ else:
220
+ raise refresh_error
221
+ except Exception as commit_error:
222
+ # If commit fails due to version string issue, try raw SQL
223
+ db.rollback()
224
+ error_str = str(commit_error)
225
+ if "Could not determine version" in error_str:
226
+ # Try using raw SQL to insert
227
+ from sqlalchemy import text
228
+ try:
229
+ result = db.execute(
230
+ text("""
231
+ INSERT INTO assets (name, file_path, file_type, product_category, sub_category, size, user_id, created_at)
232
+ VALUES (:name, :file_path, :file_type, :product_category, :sub_category, :size, :user_id, NOW())
233
+ RETURNING id, created_at
234
+ """),
235
+ {
236
+ "name": file.filename,
237
+ "file_path": str(file_path),
238
+ "file_type": file_type,
239
+ "product_category": product_category or "ocr",
240
+ "sub_category": sub_category if sub_category and sub_category != "none" else None,
241
+ "size": file_size,
242
+ "user_id": 1
243
+ }
244
+ )
245
+ row = result.fetchone()
246
+ if row:
247
+ db_asset.id = row[0]
248
+ db_asset.created_at = row[1]
249
+ db.commit()
250
+ except Exception as sql_error:
251
+ print(f"Raw SQL insert also failed: {sql_error}")
252
+ raise commit_error
253
+ else:
254
+ raise commit_error
255
 
256
  return {
257
  "id": db_asset.id,
 
260
  "product_category": db_asset.product_category,
261
  "sub_category": db_asset.sub_category,
262
  "size": db_asset.size,
263
+ "created_at": db_asset.created_at.isoformat() if hasattr(db_asset, 'created_at') else datetime.utcnow().isoformat()
264
  }
265
  except Exception as db_error:
266
  # If database save fails, still return success (file is saved)
 
279
  raise HTTPException(status_code=500, detail=str(e))
280
 
281
  @app.get("/api/assets", response_model=List[AssetResponse])
282
+ async def get_assets(
283
+ product_category: Optional[str] = None,
284
+ db: Session = Depends(get_db)
285
+ ):
286
  """Get list of assets"""
287
+ try:
288
+ from app.models import Asset
289
+ from sqlalchemy import text
290
+
291
+ # Try using ORM first
292
+ try:
293
+ query = db.query(Asset)
294
+
295
+ if product_category and product_category != "all":
296
+ query = query.filter(Asset.product_category == product_category)
297
+
298
+ db_assets = query.order_by(Asset.created_at.desc()).all()
299
+
300
+ # Convert to response format
301
+ assets = []
302
+ for asset in db_assets:
303
+ assets.append({
304
+ "id": asset.id,
305
+ "name": asset.name,
306
+ "file_type": asset.file_type,
307
+ "product_category": asset.product_category,
308
+ "sub_category": asset.sub_category,
309
+ "size": asset.size,
310
+ "created_at": asset.created_at
311
+ })
312
+ except Exception as orm_error:
313
+ # If ORM fails due to version string issue, use raw SQL
314
+ print(f"ORM query warning, using raw SQL: {orm_error}")
315
+ try:
316
+ sql_query = "SELECT id, name, file_path, file_type, product_category, sub_category, size, created_at FROM assets"
317
+ params = {}
318
+ if product_category and product_category != "all":
319
+ sql_query += " WHERE product_category = :product_category"
320
+ params["product_category"] = product_category
321
+ sql_query += " ORDER BY created_at DESC"
322
+
323
+ result = db.execute(text(sql_query), params)
324
+ rows = result.fetchall()
325
+
326
+ assets = []
327
+ for row in rows:
328
+ assets.append({
329
+ "id": row[0],
330
+ "name": row[1],
331
+ "file_type": row[3],
332
+ "product_category": row[4],
333
+ "sub_category": row[5],
334
+ "size": row[6],
335
+ "created_at": row[7]
336
+ })
337
+ except Exception as sql_error:
338
+ print(f"Raw SQL query also failed: {sql_error}")
339
+ assets = []
340
+
341
+ # Merge with mock data (as requested - keep dummy content)
342
+ mock_assets = [
343
+ {
344
+ "id": 9991,
345
+ "name": "OCR_Demo_Screenshot.png",
346
+ "file_type": "image",
347
+ "product_category": "ocr",
348
+ "sub_category": None,
349
+ "size": 2516582,
350
+ "created_at": datetime(2024, 12, 20)
351
+ },
352
+ {
353
+ "id": 9992,
354
+ "name": "P2P_Workflow_Diagram.pdf",
355
+ "file_type": "document",
356
+ "product_category": "p2p",
357
+ "sub_category": "Budget Approval Workflow",
358
+ "size": 1024000,
359
+ "created_at": datetime(2024, 12, 19)
360
+ },
361
+ {
362
+ "id": 9993,
363
+ "name": "O2C_Process_Video.mp4",
364
+ "file_type": "video",
365
+ "product_category": "o2c",
366
+ "sub_category": "Sales Order Workflow",
367
+ "size": 15728640,
368
+ "created_at": datetime(2024, 12, 18)
369
+ }
370
+ ]
371
+
372
+ # Combine real assets with mock assets (real assets first)
373
+ return assets + mock_assets
374
+ except Exception as e:
375
+ # If database query fails, return mock data only
376
+ print(f"Database query warning: {e}")
377
+ return [
378
+ {
379
+ "id": 1,
380
+ "name": "OCR_Demo_Screenshot.png",
381
+ "file_type": "image",
382
+ "product_category": "ocr",
383
+ "sub_category": None,
384
+ "size": 2516582,
385
+ "created_at": datetime.utcnow()
386
+ }
387
+ ]
388
 
389
  # ---- Post Management ----
390
 
frontend/src/pages/Repository.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef } from 'react';
2
  import { motion, AnimatePresence } from 'framer-motion';
3
  import {
4
  Upload,
@@ -104,6 +104,8 @@ export default function Repository() {
104
  const [uploadProductCategory, setUploadProductCategory] = useState('');
105
  const [uploadSubCategory, setUploadSubCategory] = useState('');
106
  const [isUploading, setIsUploading] = useState(false);
 
 
107
  const fileInputRef = useRef(null);
108
 
109
  const toggleProduct = (productId) => {
@@ -114,14 +116,14 @@ export default function Repository() {
114
  );
115
  };
116
 
117
- const filteredAssets = mockAssets.filter(asset => {
118
  const matchesSearch = asset.name.toLowerCase().includes(searchQuery.toLowerCase());
119
  const matchesProduct = selectedProduct === 'all' || asset.product === selectedProduct;
120
  return matchesSearch && matchesProduct;
121
  });
122
 
123
  const getAssetsByProduct = (productId) => {
124
- return mockAssets.filter(asset => asset.product === productId);
125
  };
126
 
127
  const getTypeIcon = (type) => {
@@ -138,6 +140,57 @@ export default function Repository() {
138
  return product?.color || 'slate';
139
  };
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  const handleFileSelect = (files) => {
142
  const fileArray = Array.from(files);
143
  setSelectedFiles(fileArray);
@@ -202,8 +255,8 @@ export default function Repository() {
202
  // Show success message
203
  alert(`Successfully uploaded ${selectedFiles.length} file(s)!`);
204
 
205
- // In a real app, you'd refresh the assets list here
206
- // For now, the dummy content will still show
207
  } catch (error) {
208
  console.error('Upload error:', error);
209
  alert(`Upload failed: ${error.message}`);
 
1
+ import React, { useState, useRef, useEffect } from 'react';
2
  import { motion, AnimatePresence } from 'framer-motion';
3
  import {
4
  Upload,
 
104
  const [uploadProductCategory, setUploadProductCategory] = useState('');
105
  const [uploadSubCategory, setUploadSubCategory] = useState('');
106
  const [isUploading, setIsUploading] = useState(false);
107
+ const [assets, setAssets] = useState(mockAssets);
108
+ const [isLoadingAssets, setIsLoadingAssets] = useState(false);
109
  const fileInputRef = useRef(null);
110
 
111
  const toggleProduct = (productId) => {
 
116
  );
117
  };
118
 
119
+ const filteredAssets = assets.filter(asset => {
120
  const matchesSearch = asset.name.toLowerCase().includes(searchQuery.toLowerCase());
121
  const matchesProduct = selectedProduct === 'all' || asset.product === selectedProduct;
122
  return matchesSearch && matchesProduct;
123
  });
124
 
125
  const getAssetsByProduct = (productId) => {
126
+ return assets.filter(asset => asset.product === productId);
127
  };
128
 
129
  const getTypeIcon = (type) => {
 
140
  return product?.color || 'slate';
141
  };
142
 
143
+ // Fetch assets from API
144
+ const fetchAssets = async () => {
145
+ setIsLoadingAssets(true);
146
+ try {
147
+ const response = await fetch('/api/assets');
148
+ if (response.ok) {
149
+ const data = await response.json();
150
+ // Convert API response to match mockAssets format
151
+ const formattedAssets = data.map(asset => ({
152
+ id: asset.id,
153
+ name: asset.name,
154
+ type: asset.file_type,
155
+ product: asset.product_category,
156
+ subCategory: asset.sub_category,
157
+ size: formatFileSize(asset.size),
158
+ date: asset.created_at ? new Date(asset.created_at).toISOString().split('T')[0] : new Date().toISOString().split('T')[0]
159
+ }));
160
+ setAssets(formattedAssets);
161
+ } else {
162
+ console.error('Failed to fetch assets');
163
+ // Keep mockAssets on error
164
+ }
165
+ } catch (error) {
166
+ console.error('Error fetching assets:', error);
167
+ // Keep mockAssets on error
168
+ } finally {
169
+ setIsLoadingAssets(false);
170
+ }
171
+ };
172
+
173
+ // Format file size
174
+ const formatFileSize = (bytes) => {
175
+ if (!bytes) return '0 B';
176
+ const k = 1024;
177
+ const sizes = ['B', 'KB', 'MB', 'GB'];
178
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
179
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
180
+ };
181
+
182
+ // Fetch assets on component mount and when upload dialog closes
183
+ useEffect(() => {
184
+ fetchAssets();
185
+ }, []);
186
+
187
+ // Refresh assets after successful upload
188
+ useEffect(() => {
189
+ if (!uploadDialogOpen && !isUploading) {
190
+ fetchAssets();
191
+ }
192
+ }, [uploadDialogOpen, isUploading]);
193
+
194
  const handleFileSelect = (files) => {
195
  const fileArray = Array.from(files);
196
  setSelectedFiles(fileArray);
 
255
  // Show success message
256
  alert(`Successfully uploaded ${selectedFiles.length} file(s)!`);
257
 
258
+ // Refresh assets list to show newly uploaded files
259
+ await fetchAssets();
260
  } catch (error) {
261
  console.error('Upload error:', error);
262
  alert(`Upload failed: ${error.message}`);