Seth commited on
Commit
059e4b7
·
1 Parent(s): 32b78fe
backend/app/main.py CHANGED
@@ -179,23 +179,36 @@ async def generate_ai_content(request: AIContentRequest, db: Session = Depends(g
179
  # ---- Asset Management ----
180
 
181
  @app.get("/api/assets/{asset_id}/status")
182
- async def get_asset_status(asset_id: int, db: Session = Depends(get_db)):
183
  """Get the analysis status of an asset"""
184
  try:
 
 
 
 
 
 
185
  from app.models import Asset
186
  conn = get_direct_psycopg2_connection()
187
- if conn:
 
 
 
 
 
188
  try:
189
- cursor = conn.cursor()
190
- # First check if extracted_content column exists
191
  cursor.execute("""
192
  SELECT column_name
193
  FROM information_schema.columns
194
  WHERE table_name='assets' AND column_name='extracted_content'
195
  """)
196
  has_extracted_content = cursor.fetchone() is not None
197
-
198
- # Build query based on column existence
 
 
 
 
199
  if has_extracted_content:
200
  cursor.execute("""
201
  SELECT id, name, analysis_status, analyzed_at, extracted_content
@@ -208,38 +221,52 @@ async def get_asset_status(asset_id: int, db: Session = Depends(get_db)):
208
  FROM assets
209
  WHERE id = %s
210
  """, (asset_id,))
211
-
212
- row = cursor.fetchone()
213
  cursor.close()
214
  conn.close()
215
-
216
- if row:
217
- result = {
218
- "asset_id": row[0],
219
- "name": row[1],
220
- "status": row[2] or "pending",
221
- "analyzed_at": row[3].isoformat() if row[3] else None,
222
- }
223
- # Add extracted_content only if column exists and value is present
224
- if has_extracted_content and len(row) > 4:
225
- result["extracted_content"] = row[4]
226
- else:
227
- result["extracted_content"] = None
228
- return result
 
 
229
  else:
230
- raise HTTPException(status_code=404, detail="Asset not found")
231
- except HTTPException:
232
- raise
233
- except Exception as e:
234
- if conn:
 
 
 
 
 
 
 
235
  conn.close()
236
- print(f"Error in get_asset_status: {e}")
237
- raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
238
- raise HTTPException(status_code=500, detail="Database connection failed")
 
 
 
239
  except HTTPException:
240
  raise
241
  except Exception as e:
242
- print(f"Error in get_asset_status (outer): {e}")
 
 
243
  raise HTTPException(status_code=500, detail=str(e))
244
 
245
  async def analyze_asset_background(asset_id: int, file_path: str, file_type: str):
@@ -448,11 +475,11 @@ async def upload_asset(
448
  ))
449
  row = cursor.fetchone()
450
  conn.commit()
451
- cursor.close()
452
- conn.close()
453
  if row:
454
- db_asset.id = row[0]
455
  db_asset.created_at = row[1]
 
 
456
  except Exception as psycopg2_error:
457
  print(f"Direct psycopg2 insert failed: {psycopg2_error}")
458
  if conn:
 
179
  # ---- Asset Management ----
180
 
181
  @app.get("/api/assets/{asset_id}/status")
182
+ async def get_asset_status(asset_id, db: Session = Depends(get_db)):
183
  """Get the analysis status of an asset"""
184
  try:
185
+ # Convert asset_id to int (handle large numbers from CockroachDB)
186
+ try:
187
+ asset_id = int(asset_id)
188
+ except (ValueError, TypeError):
189
+ raise HTTPException(status_code=400, detail=f"Invalid asset ID: {asset_id}")
190
+
191
  from app.models import Asset
192
  conn = get_direct_psycopg2_connection()
193
+ if not conn:
194
+ raise HTTPException(status_code=500, detail="Database connection failed")
195
+
196
+ try:
197
+ cursor = conn.cursor()
198
+ # First check if extracted_content column exists
199
  try:
 
 
200
  cursor.execute("""
201
  SELECT column_name
202
  FROM information_schema.columns
203
  WHERE table_name='assets' AND column_name='extracted_content'
204
  """)
205
  has_extracted_content = cursor.fetchone() is not None
206
+ except Exception as col_check_error:
207
+ print(f"Column check error (non-fatal): {col_check_error}")
208
+ has_extracted_content = False
209
+
210
+ # Build query based on column existence
211
+ try:
212
  if has_extracted_content:
213
  cursor.execute("""
214
  SELECT id, name, analysis_status, analyzed_at, extracted_content
 
221
  FROM assets
222
  WHERE id = %s
223
  """, (asset_id,))
224
+ except Exception as query_error:
225
+ print(f"Query error for asset_id {asset_id}: {query_error}")
226
  cursor.close()
227
  conn.close()
228
+ raise HTTPException(status_code=500, detail=f"Query failed: {str(query_error)}")
229
+
230
+ row = cursor.fetchone()
231
+ cursor.close()
232
+ conn.close()
233
+
234
+ if row:
235
+ result = {
236
+ "asset_id": row[0],
237
+ "name": row[1],
238
+ "status": row[2] or "pending",
239
+ "analyzed_at": row[3].isoformat() if row[3] else None,
240
+ }
241
+ # Add extracted_content only if column exists and value is present
242
+ if has_extracted_content and len(row) > 4:
243
+ result["extracted_content"] = row[4]
244
  else:
245
+ result["extracted_content"] = None
246
+ return result
247
+ else:
248
+ # Log for debugging
249
+ print(f"Asset not found: id={asset_id}, type={type(asset_id)}")
250
+ raise HTTPException(status_code=404, detail=f"Asset not found: {asset_id}")
251
+ except HTTPException:
252
+ raise
253
+ except Exception as e:
254
+ if conn:
255
+ try:
256
+ cursor.close()
257
  conn.close()
258
+ except:
259
+ pass
260
+ print(f"Error in get_asset_status for asset_id {asset_id}: {e}")
261
+ import traceback
262
+ print(traceback.format_exc())
263
+ raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
264
  except HTTPException:
265
  raise
266
  except Exception as e:
267
+ print(f"Error in get_asset_status (outer) for asset_id {asset_id}: {e}")
268
+ import traceback
269
+ print(traceback.format_exc())
270
  raise HTTPException(status_code=500, detail=str(e))
271
 
272
  async def analyze_asset_background(asset_id: int, file_path: str, file_type: str):
 
475
  ))
476
  row = cursor.fetchone()
477
  conn.commit()
 
 
478
  if row:
479
+ db_asset.id = int(row[0]) # Ensure ID is int
480
  db_asset.created_at = row[1]
481
+ cursor.close()
482
+ conn.close()
483
  except Exception as psycopg2_error:
484
  print(f"Direct psycopg2 insert failed: {psycopg2_error}")
485
  if conn:
frontend/src/pages/Repository.jsx CHANGED
@@ -279,12 +279,18 @@ export default function Repository() {
279
  const pollAssetStatus = async (assetId, fileName) => {
280
  const maxAttempts = 60; // Poll for up to 60 seconds
281
  let attempts = 0;
 
 
 
 
 
282
 
283
  while (attempts < maxAttempts) {
284
  try {
285
  const response = await fetch(`/api/assets/${assetId}/status`);
286
  if (response.ok) {
287
  const status = await response.json();
 
288
  setUploadProgress(prev => ({
289
  ...prev,
290
  [fileName]: {
@@ -297,9 +303,28 @@ export default function Repository() {
297
  if (status.status === 'completed' || status.status === 'failed') {
298
  break;
299
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  }
301
  } catch (error) {
302
  console.error('Error polling status:', error);
 
303
  }
304
 
305
  await new Promise(resolve => setTimeout(resolve, 1000)); // Poll every second
 
279
  const pollAssetStatus = async (assetId, fileName) => {
280
  const maxAttempts = 60; // Poll for up to 60 seconds
281
  let attempts = 0;
282
+ let consecutive404s = 0;
283
+ const max404s = 5; // Allow up to 5 consecutive 404s (asset might not be visible yet)
284
+
285
+ // Small delay before first poll to allow database commit
286
+ await new Promise(resolve => setTimeout(resolve, 500));
287
 
288
  while (attempts < maxAttempts) {
289
  try {
290
  const response = await fetch(`/api/assets/${assetId}/status`);
291
  if (response.ok) {
292
  const status = await response.json();
293
+ consecutive404s = 0; // Reset 404 counter on success
294
  setUploadProgress(prev => ({
295
  ...prev,
296
  [fileName]: {
 
303
  if (status.status === 'completed' || status.status === 'failed') {
304
  break;
305
  }
306
+ } else if (response.status === 404) {
307
+ consecutive404s++;
308
+ // If we get too many 404s, stop polling (asset might not exist)
309
+ if (consecutive404s >= max404s) {
310
+ console.warn(`Asset ${assetId} not found after ${max404s} attempts`);
311
+ setUploadProgress(prev => ({
312
+ ...prev,
313
+ [fileName]: {
314
+ status: 'pending',
315
+ message: 'Asset status unavailable'
316
+ }
317
+ }));
318
+ break;
319
+ }
320
+ // For first few 404s, keep trying (might be replication lag)
321
+ } else {
322
+ // Other errors - log but keep trying
323
+ console.error(`Status check failed: ${response.status}`);
324
  }
325
  } catch (error) {
326
  console.error('Error polling status:', error);
327
+ // On network errors, keep trying
328
  }
329
 
330
  await new Promise(resolve => setTimeout(resolve, 1000)); // Poll every second