LogicGoInfotechSpaces commited on
Commit
80ddd9c
·
verified ·
1 Parent(s): f9d7ba8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +248 -165
app.py CHANGED
@@ -1,4 +1,6 @@
1
 
 
 
2
  # app.py
3
  import uvicorn
4
  import numpy as np
@@ -25,8 +27,7 @@ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
25
 
26
  FOLDER = "OCR_Images"
27
 
28
- CATEGORY_API_URL = os.getenv("CATEGORY_API_URL")
29
- NOTES_CATEGORIZER_URL = os.getenv("NOTES_CATEGORIZER_URL")
30
 
31
  if not OPENAI_API_KEY:
32
  raise RuntimeError("OPENAI_API_KEY missing!")
@@ -214,7 +215,6 @@ async def generate(
214
  "type": "object",
215
  "properties": {
216
  "total_amount": {"type": "number"},
217
- "label": {"type": "string"},
218
  "date": {"type": "string"},
219
  "time": {"type": "string"},
220
  "payment_type": {
@@ -223,7 +223,7 @@ async def generate(
223
  },
224
  "notes": {"type": "string"}
225
  },
226
- "required": ["total_amount", "label"]
227
  }
228
  }
229
 
@@ -237,7 +237,7 @@ Extract expense details from OCR text below:
237
  Rules:
238
  - Do not guess missing values → use "unknown"
239
  - Notes format:
240
- "Spent <total_amount> on <label> on <date>."
241
  """
242
 
243
  response = client.chat.completions.create(
@@ -254,27 +254,47 @@ Rules:
254
 
255
  parsed.setdefault("date", "unknown")
256
  parsed.setdefault("time", "unknown")
257
- parsed.setdefault("payment_type", "unknown")
258
  parsed.setdefault("notes", "unknown")
259
 
 
 
 
 
260
  # -------- CATEGORY API --------
261
  try:
262
  cat_response = requests.post(
263
- NOTES_CATEGORIZER_URL,
264
- json={"notes": parsed["notes"]},
 
 
 
265
  timeout=10
266
  )
267
 
268
  if cat_response.status_code == 200:
269
  cat_data = cat_response.json()
270
- parsed["category"] = cat_data.get("subcategory", "unknown")
271
- parsed["category_title"] = cat_data.get("title")
 
 
 
 
 
 
 
 
 
272
  else:
273
- parsed["category"] = "unknown"
 
 
274
  parsed["category_title"] = None
275
 
276
  except Exception:
277
- parsed["category"] = "unknown"
 
 
278
  parsed["category_title"] = None
279
 
280
  response_time = time.time() - start_time
@@ -313,19 +333,21 @@ def ping():
313
  if __name__ == "__main__":
314
  uvicorn.run("app:app", host="0.0.0.0", port=7860)
315
 
316
-
317
- # # app.py
318
  # import uvicorn
319
  # import numpy as np
320
  # import cv2
321
  # import boto3
322
  # import os
323
  # import json
 
324
  # import requests
325
- # from fastapi import FastAPI, UploadFile, File, HTTPException
 
326
  # from rapidocr_onnxruntime import RapidOCR
327
  # from openai import OpenAI
328
-
 
 
329
  # # ---------------- ENV CONFIG ----------------
330
  # DO_KEY_ID = os.getenv("DO_SPACES_KEY_ID")
331
  # DO_SECRET_KEY = os.getenv("DO_SPACES_SECRET_KEY")
@@ -336,16 +358,16 @@ if __name__ == "__main__":
336
 
337
  # FOLDER = "OCR_Images"
338
 
 
 
 
339
  # if not OPENAI_API_KEY:
340
  # raise RuntimeError("OPENAI_API_KEY missing!")
341
 
 
342
  # client = OpenAI(api_key=OPENAI_API_KEY)
343
 
344
-
345
- # CATEGORY_API_URL = os.getenv("CATEGORY_API_URL")
346
- # NOTES_CATEGORIZER_URL = os.getenv("NOTES_CATEGORIZER_URL")
347
-
348
- # # S3 client
349
  # s3 = boto3.client(
350
  # "s3",
351
  # region_name=DO_REGION,
@@ -354,14 +376,92 @@ if __name__ == "__main__":
354
  # aws_secret_access_key=DO_SECRET_KEY,
355
  # )
356
 
 
 
 
 
 
 
 
357
  # app = FastAPI()
358
  # ocr_engine = RapidOCR()
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  # # ---------------- ROUTES ----------------
361
  # @app.get("/health")
362
  # async def health():
363
- # return {"status": "ok"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
  # @app.post("/upload")
367
  # async def upload_image(file: UploadFile = File(...)):
@@ -377,113 +477,102 @@ if __name__ == "__main__":
377
  # ACL="private"
378
  # )
379
 
380
- # # Also return a local path (if available) for debugging / local testing.
381
- # # Developer note: we include a local container path at /mnt/data/image.png when applicable.
382
- # return {"image_id": image_key, "message": "Uploaded successfully", "local_path": "/mnt/data/image.png"}
 
 
 
383
 
384
  # except Exception as e:
385
  # raise HTTPException(status_code=500, detail=str(e))
386
 
387
-
388
  # @app.post("/generate/{image_id:path}")
389
- # async def generate(image_id: str):
 
 
 
 
390
 
391
- # # -------- Download image --------
392
  # try:
393
- # obj = s3.get_object(Bucket=DO_BUCKET, Key=image_id)
394
- # raw_bytes = obj["Body"].read()
395
- # except Exception:
396
- # # Fallback: try to load from local path if exists (useful for local testing)
397
- # local_path = "/mnt/data/image.png"
398
- # if os.path.exists(local_path):
399
- # with open(local_path, "rb") as f:
400
- # raw_bytes = f.read()
401
- # else:
402
- # raise HTTPException(status_code=404, detail="Image not found")
403
-
404
- # img_array = np.frombuffer(raw_bytes, np.uint8)
405
- # img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
406
- # if img is None:
407
- # raise HTTPException(status_code=400, detail="Unable to decode image")
408
-
409
- # # -------- OCR --------
410
- # result, _ = ocr_engine(img)
411
- # if not result:
412
- # raise HTTPException(status_code=500, detail="OCR returned empty result")
413
-
414
- # full_text = "\n".join([text for _, text, _ in result])
415
-
416
- # # -------- CONFIDENCE SCORE --------
417
- # confidences = [conf for _, _, conf in result if isinstance(conf, (int, float))]
418
- # avg_confidence = sum(confidences) / len(confidences) if confidences else 0
419
-
420
- # if avg_confidence < 0.70:
421
- # return {
422
- # "image_id": image_id,
423
- # "raw_text": full_text,
424
- # "confidence": round(avg_confidence, 3),
425
- # "message": "Upload image with more clarity or enter manually.",
426
- # "source_image_path": "/mnt/data/image.png"
427
- # }
428
-
429
- # # -------- JSON SCHEMA FOR GPT --------
430
- # schema = {
431
- # "name": "extract_expense_details",
432
- # "schema": {
433
- # "type": "object",
434
- # "properties": {
435
- # "total_amount": {"type": "number"},
436
- # "label": {"type": "string"},
437
- # "date": {"type": "string"},
438
- # "time": {"type": "string"},
439
- # "payment_type": {
440
- # "type": "string",
441
- # "enum": ["cash", "card", "upi", "unknown"]
 
 
 
 
 
 
 
 
 
 
 
442
  # },
443
- # "notes": {"type": "string"}
444
- # },
445
- # "required": ["total_amount", "label"]
446
  # }
447
- # }
448
-
449
- # # -------- PROMPT --------
450
- # prompt = f"""
451
- # You are an expense extraction AI.
452
 
453
- # Extract expense details from the OCR text below:
 
454
 
455
  # \"\"\"
456
  # {full_text}
457
  # \"\"\"
458
 
459
- # ### STRICT INFORMATION RULES:
460
- # - Do NOT create or guess any information that does not exist in the extracted text.
461
- # - If any field (date, time, payment_type, total_amount) is not clearly present in the text, set its value to "unknown".
462
- # - Only infer the label category (Restaurant, Store, etc.) based on business name and item types.
463
-
464
- # ### Labeling Rules:
465
- # 1. Detect the business/merchant name from the text (e.g., KFC, Starbucks, Ying Thai Kitchen).
466
- # 2. If items are food or restaurant-related → label must be: "<Business Name> Restaurant".
467
- # 3. If it's a store/retail → "<Business Name> Store".
468
- # 4. If unclear, infer the closest meaningful category.
469
- # 5. If business name is not found → label = "unknown".
470
-
471
- # ### Notes Format:
472
- # Always generate notes EXACTLY in this format:
473
  # "Spent <total_amount> on <label> on <date>."
474
-
475
- # ### Required Output:
476
- # Return structured JSON (via schema) with:
477
- # - total_amount
478
- # - label
479
- # - date
480
- # - time
481
- # - payment_type
482
- # - notes
483
  # """
484
 
485
- # # -------- CALL GPT --------
486
- # try:
487
  # response = client.chat.completions.create(
488
  # model="gpt-4o-mini",
489
  # response_format={"type": "json_schema", "json_schema": schema},
@@ -494,71 +583,65 @@ if __name__ == "__main__":
494
  # temperature=0.1
495
  # )
496
 
497
- # # The SDK may return the json directly in a field depending on version;
498
- # # fall back to extracting message content.
499
- # raw_content = None
 
 
 
 
 
500
  # try:
501
- # raw_content = response.choices[0].message.content
502
- # parsed = json.loads(raw_content)
 
 
 
 
 
 
 
 
 
 
 
 
503
  # except Exception:
504
- # # try another path if SDK embeds the json directly
505
- # try:
506
- # parsed = response.choices[0].message.json # hypothetical
507
- # except Exception:
508
- # raise
509
 
510
- # except Exception as e:
511
- # raise HTTPException(status_code=500, detail=f"OpenAI Error: {str(e)}")
 
 
 
 
 
512
 
513
- # # Ensure required keys exist and enforce strict defaults
514
- # parsed.setdefault("total_amount", 0)
515
- # parsed.setdefault("label", "unknown")
516
- # parsed.setdefault("date", "unknown")
517
- # parsed.setdefault("time", "unknown")
518
- # parsed.setdefault("payment_type", "unknown")
519
- # parsed.setdefault("notes", "unknown")
 
520
 
521
- # # -------- CATEGORY API CALL (USING NOTES INSTEAD OF LABEL) --------
522
- # # Use the notes text to derive a category/subcategory via the notes categorizer.
523
- # notes_text = parsed.get("notes", "")
524
 
525
- # try:
526
- # cat_response = requests.post(
527
- # NOTES_CATEGORIZER_URL,
528
- # json={"notes": notes_text},
529
- # timeout=10
530
  # )
531
 
532
- # if cat_response.status_code == 200:
533
- # cat_data = cat_response.json()
534
- # # category should be filled with the subcategory field from the notes API
535
- # parsed["category"] = cat_data.get("subcategory", "unknown")
536
- # # keep label unchanged
537
- # parsed["label"] = parsed.get("label", "unknown")
538
- # # also provide the top-level title for convenience
539
- # parsed["category_title"] = cat_data.get("title", None)
540
- # else:
541
- # parsed["category"] = "unknown"
542
- # parsed["category_title"] = None
543
 
544
- # except Exception:
545
- # parsed["category"] = "unknown"
546
- # parsed["category_title"] = None
547
-
548
- # # -------- FINAL RESPONSE --------
549
- # return {
550
- # "image_id": image_id,
551
- # "raw_text": full_text,
552
- # "confidence": round(avg_confidence, 3),
553
- # "parsed": parsed,
554
- # # Developer/test helper: include local path (will be transformed if necessary)
555
- # "source_image_path": "/mnt/data/image.png"
556
- # }
557
-
558
  # @app.get("/ping")
559
  # def ping():
560
  # return {"status": "alive"}
561
 
562
-
563
  # if __name__ == "__main__":
564
- # uvicorn.run("app:app", host="0.0.0.0", port=7860)
 
1
 
2
+ # # app.py
3
+
4
  # app.py
5
  import uvicorn
6
  import numpy as np
 
27
 
28
  FOLDER = "OCR_Images"
29
 
30
+ CATEGORY_API_URL = "https://logicgoinfotechspaces-expensecategorizenotes.hf.space"
 
31
 
32
  if not OPENAI_API_KEY:
33
  raise RuntimeError("OPENAI_API_KEY missing!")
 
215
  "type": "object",
216
  "properties": {
217
  "total_amount": {"type": "number"},
 
218
  "date": {"type": "string"},
219
  "time": {"type": "string"},
220
  "payment_type": {
 
223
  },
224
  "notes": {"type": "string"}
225
  },
226
+ "required": ["total_amount"]
227
  }
228
  }
229
 
 
237
  Rules:
238
  - Do not guess missing values → use "unknown"
239
  - Notes format:
240
+ "Spent <total_amount> on <merchant_name> on <date>."
241
  """
242
 
243
  response = client.chat.completions.create(
 
254
 
255
  parsed.setdefault("date", "unknown")
256
  parsed.setdefault("time", "unknown")
257
+ parsed.setdefault("payment_type", "cash")
258
  parsed.setdefault("notes", "unknown")
259
 
260
+ # Set payment_type to "cash" if it's "unknown"
261
+ if parsed.get("payment_type") == "unknown":
262
+ parsed["payment_type"] = "cash"
263
+
264
  # -------- CATEGORY API --------
265
  try:
266
  cat_response = requests.post(
267
+ f"{CATEGORY_API_URL}/api/v1/categorize",
268
+ json={
269
+ "notes": parsed["notes"],
270
+ "user_id": user_id
271
+ },
272
  timeout=10
273
  )
274
 
275
  if cat_response.status_code == 200:
276
  cat_data = cat_response.json()
277
+ if cat_data.get("status") == "success" and cat_data.get("data"):
278
+ data = cat_data["data"]
279
+ parsed["headcategory_id"] = data.get("headcategory_id")
280
+ parsed["headcategory_title"] = data.get("headcategory_title")
281
+ parsed["category_id"] = data.get("category_id")
282
+ parsed["category_title"] = data.get("category_title")
283
+ else:
284
+ parsed["headcategory_id"] = None
285
+ parsed["headcategory_title"] = None
286
+ parsed["category_id"] = None
287
+ parsed["category_title"] = None
288
  else:
289
+ parsed["headcategory_id"] = None
290
+ parsed["headcategory_title"] = None
291
+ parsed["category_id"] = None
292
  parsed["category_title"] = None
293
 
294
  except Exception:
295
+ parsed["headcategory_id"] = None
296
+ parsed["headcategory_title"] = None
297
+ parsed["category_id"] = None
298
  parsed["category_title"] = None
299
 
300
  response_time = time.time() - start_time
 
333
  if __name__ == "__main__":
334
  uvicorn.run("app:app", host="0.0.0.0", port=7860)
335
 
 
 
336
  # import uvicorn
337
  # import numpy as np
338
  # import cv2
339
  # import boto3
340
  # import os
341
  # import json
342
+ # import time
343
  # import requests
344
+ # from datetime import datetime
345
+ # from fastapi import FastAPI, UploadFile, File, HTTPException, Header
346
  # from rapidocr_onnxruntime import RapidOCR
347
  # from openai import OpenAI
348
+ # from pymongo import MongoClient
349
+ # from pymongo.errors import PyMongoError
350
+ # from botocore.exceptions import BotoCoreError, ClientError
351
  # # ---------------- ENV CONFIG ----------------
352
  # DO_KEY_ID = os.getenv("DO_SPACES_KEY_ID")
353
  # DO_SECRET_KEY = os.getenv("DO_SPACES_SECRET_KEY")
 
358
 
359
  # FOLDER = "OCR_Images"
360
 
361
+ # CATEGORY_API_URL = os.getenv("CATEGORY_API_URL")
362
+ # NOTES_CATEGORIZER_URL = os.getenv("NOTES_CATEGORIZER_URL")
363
+
364
  # if not OPENAI_API_KEY:
365
  # raise RuntimeError("OPENAI_API_KEY missing!")
366
 
367
+ # # ---------------- OPENAI ----------------
368
  # client = OpenAI(api_key=OPENAI_API_KEY)
369
 
370
+ # # ---------------- S3 ----------------
 
 
 
 
371
  # s3 = boto3.client(
372
  # "s3",
373
  # region_name=DO_REGION,
 
376
  # aws_secret_access_key=DO_SECRET_KEY,
377
  # )
378
 
379
+ # # ---------------- MONGODB ----------------
380
+ # MONGO_URI = os.getenv("MONGO_URI")
381
+ # mongo_client = MongoClient(MONGO_URI)
382
+ # mongo_db = mongo_client["expense"]
383
+ # api_logs_col = mongo_db["api_logs"]
384
+
385
+ # # ---------------- APP ----------------
386
  # app = FastAPI()
387
  # ocr_engine = RapidOCR()
388
 
389
+ # # ---------------- HELPERS ----------------
390
+ # def ist_now():
391
+ # return datetime.now().strftime("%d-%m-%Y %H:%M:%S:IST")
392
+
393
+ # def log_api_event(
394
+ # *,
395
+ # status: str,
396
+ # response_time: float,
397
+ # user_id: str | None,
398
+ # error_message: str | None = None
399
+ # ):
400
+ # payload = {
401
+ # "name": "Receipt Scanner",
402
+ # "status": status,
403
+ # "date": ist_now(),
404
+ # "response_time": round(response_time, 3),
405
+ # }
406
+
407
+ # if user_id:
408
+ # payload["user_id"] = user_id
409
+
410
+ # if error_message:
411
+ # payload["error_message"] = error_message
412
+
413
+ # try:
414
+ # api_logs_col.insert_one(payload)
415
+ # except Exception:
416
+ # pass # never break API because of logging failure
417
+
418
  # # ---------------- ROUTES ----------------
419
  # @app.get("/health")
420
  # async def health():
421
+ # health_report = {
422
+ # "service": "Receipt Scanner API",
423
+ # "status": "healthy",
424
+ # "checks": {}
425
+ # }
426
+
427
+ # # ---------------- MongoDB ----------------
428
+ # try:
429
+ # mongo_client.admin.command("ping")
430
+ # health_report["checks"]["mongodb"] = "ok"
431
+ # except PyMongoError as e:
432
+ # health_report["checks"]["mongodb"] = f"fail: {str(e)}"
433
+ # health_report["status"] = "degraded"
434
+
435
+ # # ---------------- OpenAI ----------------
436
+ # try:
437
+ # # very light call, does not consume tokens
438
+ # client.models.list()
439
+ # health_report["checks"]["openai"] = "ok"
440
+ # except Exception as e:
441
+ # health_report["checks"]["openai"] = f"fail: {str(e)}"
442
+ # health_report["status"] = "degraded"
443
 
444
+ # # ---------------- DO Spaces / S3 ----------------
445
+ # try:
446
+ # s3.head_bucket(Bucket=DO_BUCKET)
447
+ # health_report["checks"]["object_storage"] = "ok"
448
+ # except (BotoCoreError, ClientError) as e:
449
+ # health_report["checks"]["object_storage"] = f"fail: {str(e)}"
450
+ # health_report["status"] = "degraded"
451
+
452
+ # # ---------------- OCR Engine ----------------
453
+ # try:
454
+ # if ocr_engine is None:
455
+ # raise RuntimeError("OCR engine not initialized")
456
+ # health_report["checks"]["ocr"] = "ok"
457
+ # except Exception as e:
458
+ # health_report["checks"]["ocr"] = f"fail: {str(e)}"
459
+ # health_report["status"] = "degraded"
460
+
461
+ # # ---------------- Overall ----------------
462
+ # health_report["timestamp"] = datetime.utcnow().isoformat()
463
+
464
+ # return health_report
465
 
466
  # @app.post("/upload")
467
  # async def upload_image(file: UploadFile = File(...)):
 
477
  # ACL="private"
478
  # )
479
 
480
+ # return {
481
+ # "status": "success",
482
+ # "message": "Uploaded successfully",
483
+ # "image_id": image_key,
484
+ # "local_path": "/mnt/data/image.png"
485
+ # }
486
 
487
  # except Exception as e:
488
  # raise HTTPException(status_code=500, detail=str(e))
489
 
 
490
  # @app.post("/generate/{image_id:path}")
491
+ # async def generate(
492
+ # image_id: str,
493
+ # user_id: str | None = Header(default=None, alias="user_id")
494
+ # ):
495
+ # start_time = time.time()
496
 
 
497
  # try:
498
+ # # -------- DOWNLOAD IMAGE --------
499
+ # try:
500
+ # obj = s3.get_object(Bucket=DO_BUCKET, Key=image_id)
501
+ # raw_bytes = obj["Body"].read()
502
+ # except Exception:
503
+ # local_path = "/mnt/data/image.png"
504
+ # if os.path.exists(local_path):
505
+ # with open(local_path, "rb") as f:
506
+ # raw_bytes = f.read()
507
+ # else:
508
+ # raise HTTPException(status_code=404, detail="Image not found")
509
+
510
+ # img_array = np.frombuffer(raw_bytes, np.uint8)
511
+ # img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
512
+
513
+ # if img is None:
514
+ # raise HTTPException(status_code=400, detail="Unable to decode image")
515
+
516
+ # # -------- OCR --------
517
+ # result, _ = ocr_engine(img)
518
+ # if not result:
519
+ # raise RuntimeError("OCR returned empty result")
520
+
521
+ # full_text = "\n".join([text for _, text, _ in result])
522
+
523
+ # confidences = [conf for _, _, conf in result if isinstance(conf, (int, float))]
524
+ # avg_confidence = sum(confidences) / len(confidences) if confidences else 0
525
+
526
+ # if avg_confidence < 0.70:
527
+ # response_time = time.time() - start_time
528
+ # log_api_event(
529
+ # status="fail",
530
+ # response_time=response_time,
531
+ # user_id=user_id,
532
+ # error_message="Low OCR confidence"
533
+ # )
534
+
535
+ # return {
536
+ # "status": "fail",
537
+ # "message": "Upload image with more clarity or enter manually.",
538
+ # "image_id": image_id,
539
+ # "raw_text": full_text,
540
+ # "confidence": round(avg_confidence, 3),
541
+ # }
542
+
543
+ # # -------- GPT SCHEMA --------
544
+ # schema = {
545
+ # "name": "extract_expense_details",
546
+ # "schema": {
547
+ # "type": "object",
548
+ # "properties": {
549
+ # "total_amount": {"type": "number"},
550
+ # "label": {"type": "string"},
551
+ # "date": {"type": "string"},
552
+ # "time": {"type": "string"},
553
+ # "payment_type": {
554
+ # "type": "string",
555
+ # "enum": ["cash", "card", "upi", "unknown"]
556
+ # },
557
+ # "notes": {"type": "string"}
558
  # },
559
+ # "required": ["total_amount", "label"]
560
+ # }
 
561
  # }
 
 
 
 
 
562
 
563
+ # prompt = f"""
564
+ # Extract expense details from OCR text below:
565
 
566
  # \"\"\"
567
  # {full_text}
568
  # \"\"\"
569
 
570
+ # Rules:
571
+ # - Do not guess missing values use "unknown"
572
+ # - Notes format:
 
 
 
 
 
 
 
 
 
 
 
573
  # "Spent <total_amount> on <label> on <date>."
 
 
 
 
 
 
 
 
 
574
  # """
575
 
 
 
576
  # response = client.chat.completions.create(
577
  # model="gpt-4o-mini",
578
  # response_format={"type": "json_schema", "json_schema": schema},
 
583
  # temperature=0.1
584
  # )
585
 
586
+ # parsed = json.loads(response.choices[0].message.content)
587
+
588
+ # parsed.setdefault("date", "unknown")
589
+ # parsed.setdefault("time", "unknown")
590
+ # parsed.setdefault("payment_type", "unknown")
591
+ # parsed.setdefault("notes", "unknown")
592
+
593
+ # # -------- CATEGORY API --------
594
  # try:
595
+ # cat_response = requests.post(
596
+ # NOTES_CATEGORIZER_URL,
597
+ # json={"notes": parsed["notes"]},
598
+ # timeout=10
599
+ # )
600
+
601
+ # if cat_response.status_code == 200:
602
+ # cat_data = cat_response.json()
603
+ # parsed["category"] = cat_data.get("subcategory", "unknown")
604
+ # parsed["category_title"] = cat_data.get("title")
605
+ # else:
606
+ # parsed["category"] = "unknown"
607
+ # parsed["category_title"] = None
608
+
609
  # except Exception:
610
+ # parsed["category"] = "unknown"
611
+ # parsed["category_title"] = None
 
 
 
612
 
613
+ # response_time = time.time() - start_time
614
+
615
+ # log_api_event(
616
+ # status="success",
617
+ # response_time=response_time,
618
+ # user_id=user_id
619
+ # )
620
 
621
+ # return {
622
+ # "status": "success",
623
+ # "message": "Receipt processed and logged in DB",
624
+ # "image_id": image_id,
625
+ # "confidence": round(avg_confidence, 3),
626
+ # "raw_text": full_text,
627
+ # "parsed": parsed,
628
+ # }
629
 
630
+ # except Exception as e:
631
+ # response_time = time.time() - start_time
 
632
 
633
+ # log_api_event(
634
+ # status="fail",
635
+ # response_time=response_time,
636
+ # user_id=user_id,
637
+ # error_message=str(e)
638
  # )
639
 
640
+ # raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  # @app.get("/ping")
643
  # def ping():
644
  # return {"status": "alive"}
645
 
 
646
  # if __name__ == "__main__":
647
+ # uvicorn.run("app:app", host="0.0.0.0", port=7860)