m8chaa commited on
Commit
2d609c7
1 Parent(s): a768a32

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +310 -386
app.py CHANGED
@@ -209,110 +209,97 @@ async def move_file_to_folder(file_id, current_parents, new_parents):
209
  logging.error(f"Failed to move file {file_id} to new folders: {e}")
210
 
211
 
212
- async def update_user_sheet(spreadsheet_id:str, sheet_id:int, sheet_name, data: List[List[str]], is_reset: Optional[bool] = False):
213
- print(f"Spreadsheet ID: {spreadsheet_id}, Sheet ID: {sheet_id}, sheet name: {sheet_name}, Data: {data}")
214
- # first identify the area to insert the data
215
- no_of_rows = len(data)
216
- no_of_cols = len(data[0])
217
-
218
- if is_reset:
219
- # sheet_name = f"{sheet_name} {datetime.now(kst).strftime("%Y-%m-%d %H:%M:%S")}"
220
- sheet_name = f"{sheet_name} {datetime.now(kst).strftime('%Y-%m-%d %H:%M')}"
221
- duplicate_requests = [{
222
- "duplicateSheet": {
223
- "sourceSheetId": contacts_ss,
224
- "newSheetName": sheet_name
225
- }
226
- }]
227
- duplicate_response = sheet_service.spreadsheets().batchUpdate(
228
- spreadsheetId=spreadsheet_id,
229
- body={
230
- 'requests': duplicate_requests
231
- }
232
- ).execute()
233
- sheet_id = duplicate_response['replies'][0]['duplicateSheet']['properties']['sheetId']
234
-
235
- # Insert new rows
236
- requests = [
237
- {
238
- "updateSheetProperties": {
239
- "properties": {
240
- "sheetId": sheet_id,
241
- "hidden": False
242
- },
243
- "fields": "hidden"
244
- }
245
- },
246
- {
247
- "insertDimension": {
248
- "range": {
249
- "sheetId": sheet_id,
250
- "dimension": "ROWS",
251
- "startIndex": 1,
252
- "endIndex": no_of_rows + 1
253
- },
254
- "inheritFromBefore": False,
255
- }
256
- }]
257
 
258
-
259
- response = sheet_service.spreadsheets().batchUpdate(
260
- spreadsheetId=spreadsheet_id,
261
- body={
262
- 'requests': requests
263
- }
264
- ).execute()
265
-
266
- update_body = {
267
- 'values': data
268
- }
269
- start_column = 'A'
270
- end_column = chr(ord(start_column) + no_of_cols - 1)
271
- range_to_update = f'{sheet_name}!{start_column}2:{end_column}{no_of_rows + 1}'
272
-
273
- result = sheet_service.spreadsheets().values().update(
274
- spreadsheetId=spreadsheet_id,
275
- range=range_to_update,
276
- valueInputOption='USER_ENTERED',
277
- body=update_body
278
- ).execute()
 
 
 
 
 
 
 
 
 
 
279
 
280
- # Format the cells
281
- for row_index, row in enumerate(data):
282
- for col_index, cell_value in enumerate(row):
283
- if cell_value.startswith('=HYPERLINK'):
284
- color = {"red": 0, "green": 0, "blue": 1} # Blue for hyperlinks
285
  else:
286
- color = {"red": 0, "green": 0, "blue": 0} # Black for other text
287
-
288
- requests = [
289
- {
290
- "repeatCell": {
291
- "range": {
292
- "sheetId": sheet_id,
293
- "startRowIndex": row_index + 1,
294
- "endRowIndex": row_index + 2,
295
- "startColumnIndex": col_index,
296
- "endColumnIndex": col_index + 1
297
- },
298
- "cell": {
299
- "userEnteredFormat": {
300
- "textFormat": {
301
- "foregroundColor": color
302
- }
303
- }
304
- },
305
- "fields": "userEnteredFormat.textFormat.foregroundColor"
306
- }
307
- }
308
- ]
309
-
310
- response = sheet_service.spreadsheets().batchUpdate(
311
- spreadsheetId=spreadsheet_id,
312
- body={
313
- 'requests': requests
314
- }
315
- ).execute()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
  async def copy_sms_sheet(phone_no: str, spreadsheet_id: str):
318
  requests = [
@@ -396,7 +383,13 @@ template_sheet_id = '1i5mrmlTs5sPWtx2mBtc_f-zm1D3x21r1T_77guki8_8'
396
  async def edit_spreadsheet(target_sheet_id: str = Form(...)):
397
  try:
398
  # Retrieve the target spreadsheet's metadata
399
- target_spreadsheet = sheet_service.spreadsheets().get(spreadsheetId=target_sheet_id).execute()
 
 
 
 
 
 
400
 
401
  # Collect all sheet IDs in the target spreadsheet
402
  target_sheet_ids = [sheet['properties']['sheetId'] for sheet in target_spreadsheet.get('sheets', [])]
@@ -411,214 +404,76 @@ async def edit_spreadsheet(target_sheet_id: str = Form(...)):
411
  })
412
 
413
  # Retrieve the template spreadsheet's metadata
414
- template_spreadsheet = sheet_service.spreadsheets().get(spreadsheetId=template_sheet_id).execute()
 
 
 
 
 
 
415
 
416
  # Iterate through each sheet in the template spreadsheet
417
  for sheet in template_spreadsheet.get('sheets', []):
418
  sheet_id = sheet['properties']['sheetId']
419
 
420
- # Use the copyTo method to copy the sheet to the target spreadsheet
421
- copy_request = sheet_service.spreadsheets().sheets().copyTo(
422
- spreadsheetId=template_sheet_id,
423
- sheetId=sheet_id,
424
- body={"destinationSpreadsheetId": target_sheet_id}
425
- )
426
- copy_response = copy_request.execute()
427
-
428
- # Get the copied sheet ID
429
- copied_sheet_id = copy_response['sheetId']
430
-
431
- # Rename the copied sheet to remove "Copy of"
432
- copied_sheet_title = sheet['properties']['title'].replace("Copy of ", "")
433
- rename_request = {
434
- "updateSheetProperties": {
435
- "properties": {
436
- "sheetId": copied_sheet_id,
437
- "title": copied_sheet_title
438
- },
439
- "fields": "title"
 
 
440
  }
441
- }
442
 
443
- # Execute rename request
444
- sheet_service.spreadsheets().batchUpdate(
445
- spreadsheetId=target_sheet_id,
446
- body={"requests": [rename_request]}
447
- ).execute()
448
 
449
- logging.info(f"Sheet {sheet['properties']['title']} copied and renamed to {copied_sheet_title} in {target_sheet_id}.")
 
 
 
450
 
451
  if requests:
452
- # Execute batch update to delete sheets
453
- batch_update_request = {"requests": requests}
454
- sheet_service.spreadsheets().batchUpdate(
455
- spreadsheetId=target_sheet_id,
456
- body=batch_update_request
457
- ).execute()
458
- logging.info(f"Deleted existing sheets in target spreadsheet {target_sheet_id}.")
 
 
 
 
459
 
460
  logging.info(f"All sheets from template {template_sheet_id} copied to {target_sheet_id}.")
461
  return {"message": "Spreadsheet copied successfully"}
462
 
 
 
 
463
  except Exception as e:
464
- logging.error(f"Failed to copy sheets: {e}")
465
- return {"error": str(e)}
466
-
467
- # image upload
468
- # receive image id and user_id
469
- # check if they have enough credits
470
- # post to gpt4o with the user credit info
471
- # identify the sheet_id to update by checking in user_id
472
- # update user sheet
473
- # update firebase transaction
474
- # privatize the image and move the image to appropriate folder(s)
475
- # @app.post("/process-image")
476
- # async def process_photo(image_id: str = Form(...), user_id: str = Form(...)):
477
- # transaction_ref = transactions_collection_ref.document(user_id)
478
- # transaction = transaction_ref.get()
479
- # # If transaction does not exist, create a new one
480
- # if not transaction.exists:
481
- # transaction_ref.set({
482
- # "no_sms": 0,
483
- # "no_contacts": 0,
484
- # "no_receipts": 0,
485
- # "no_business_cards": 0,
486
- # "purchased_credit": {
487
- # "image_detection": 100,
488
- # "sync_data": 500,
489
- # }
490
- # })
491
- # transaction = transaction_ref.get()
492
-
493
- # transaction_data = transaction.to_dict()
494
-
495
- # # check if any of the limits have been reached
496
- # if transaction_data['no_receipts'] + transaction_data['no_business_cards'] >= transaction_data['purchased_credit']['image_detection']:
497
- # return {"error": "You have reached the limit of the number of items you can process. Please upgrade your plan."}
498
-
499
- # user_ref = users_collection_ref.document(user_id)
500
- # user = user_ref.get()
501
- # user_data = user.to_dict()
502
 
503
-
504
- # try:
505
- # parent_folders = [user_data['gDrive_metadata']['yungsoogi'], user_data['gDrive_metadata']['uploaded']]
506
- # dataJson = await request_gpt4o_completion(image_id, transaction_data['purchased_credit']['image_detection'])
507
- # if dataJson is None:
508
- # return {"error": "An error occurred while processing the image"}
509
-
510
- # data = json.loads(dataJson)
511
- # # Check if 'receipts' key exists and the length
512
- # found_receipt_no = len(data['receipts']) if 'receipts' in data else 0
513
- # found_business_cards_no = len(data['busi_cards']) if 'busi_cards' in data else 0
514
- # new_folders = []
515
- # timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
516
- # # https://drive.google.com/file/d/1cFIA09PBqFjM8YvIz1D60p7HMGk1uBvo/view?usp=sharing
517
- # download_image_url = f"https://drive.google.com/uc?id={image_id}&export=download"
518
- # image_url = f"https://drive.google.com/file/d/{image_id}/view?usp=sharing"
519
- # # 이미지를 보여주어야 할때 =image({image_url})를 사용하고 갈 수 있는 경로를 만들때는 =HYPERLINK({image_url}, '바로가기')를 사용하세요.
520
- # if found_receipt_no > 0:
521
- # new_folders.append(user_data['gDrive_metadata']['receipts'])
522
- # receipts_data = convert_dicts_to_list(
523
- # data['receipts'],
524
- # add_link=True,
525
- # image_id=image_id,
526
- # link_text="바로가기",
527
- # link_position=1 # Link third
528
- # )
529
- # print(receipts_data)
530
- # await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], receipts_ss, "영수증", receipts_data)
531
- # if found_business_cards_no > 0:
532
- # new_folders.append(user_data['gDrive_metadata']['business_cards'])
533
- # business_cards_data = convert_dicts_to_list(
534
- # data['busi_cards'],
535
- # add_link=True,
536
- # image_id=image_id,
537
- # link_text="명함보기",
538
- # link_position=-1 # Link at the end
539
- # )
540
- # await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], business_cards_ss, "명함", business_cards_data)
541
-
542
- # print(f"{found_receipt_no}, {found_business_cards_no}")
543
-
544
- # status = await update_fb_transaction(
545
- # transaction_ref,
546
- # no_receipts=found_receipt_no,
547
- # no_business_cards=found_business_cards_no
548
- # )
549
-
550
-
551
- # if status['status'] == "Success":
552
- # await move_file_to_folder(image_id, parent_folders, new_folders)
553
- # return {"success": True}
554
- # else:
555
- # logging.error(f"Transaction update failed with status: {status['status']}")
556
- # return {"error": "An error has occurred while updating the transaction"}
557
- # except Exception as e:
558
- # logging.error(f"An error occurred: {e}")
559
- # # Move the image file to the error folder
560
- # await move_file_to_folder(image_id, parent_folders, user_data['gDrive_metadata']['error'])
561
- # return {"error": str(e)}
562
-
563
- # async def update_user_sheets_and_folders(user_data, transaction_ref, transaction_data, image_id):
564
- # try:
565
- # parent_folders = [user_data['gDrive_metadata']['yungsoogi'], user_data['gDrive_metadata']['uploaded']]
566
- # dataJson = await request_gpt4o_completion(image_id, transaction_data['purchased_credit']['image_detection'])
567
- # if dataJson is None:
568
- # return {"error": "An error occurred while processing the image"}
569
-
570
- # data = json.loads(dataJson)
571
- # # Check if 'receipts' key exists and the length
572
- # found_receipt_no = len(data['receipts']) if 'receipts' in data else 0
573
- # found_business_cards_no = len(data['busi_cards']) if 'busi_cards' in data else 0
574
- # new_folders = []
575
- # timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
576
- # # https://drive.google.com/file/d/1cFIA09PBqFjM8YvIz1D60p7HMGk1uBvo/view?usp=sharing
577
- # download_image_url = f"https://drive.google.com/uc?id={image_id}&export=download"
578
- # image_url = f"https://drive.google.com/file/d/{image_id}/view?usp=sharing"
579
- # # 이미지를 보여주어야 할때 =image({image_url})를 사용하고 갈 수 있는 경로를 만들때는 =HYPERLINK({image_url}, '바로가기')를 사용하세요.
580
- # if found_receipt_no > 0:
581
- # new_folders.append(user_data['gDrive_metadata']['receipts'])
582
- # receipts_data = convert_dicts_to_list(
583
- # data['receipts'],
584
- # add_link=True,
585
- # image_id=image_id,
586
- # link_text="바로가기",
587
- # link_position=1 # Link third
588
- # )
589
- # print(receipts_data)
590
- # await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], receipts_ss, "영수증", receipts_data)
591
- # if found_business_cards_no > 0:
592
- # new_folders.append(user_data['gDrive_metadata']['business_cards'])
593
- # business_cards_data = convert_dicts_to_list(
594
- # data['busi_cards'],
595
- # add_link=True,
596
- # image_id=image_id,
597
- # link_text="명함보기",
598
- # link_position=-1 # Link at the end
599
- # )
600
- # await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], business_cards_ss, "명함", business_cards_data)
601
-
602
- # print(f"{found_receipt_no}, {found_business_cards_no}")
603
-
604
- # status = await update_fb_transaction(
605
- # transaction_ref,
606
- # no_receipts=found_receipt_no,
607
- # no_business_cards=found_business_cards_no
608
- # )
609
-
610
-
611
- # if status['status'] == "Success":
612
- # await move_file_to_folder(image_id, parent_folders, new_folders)
613
- # return {"success": True}
614
- # else:
615
- # logging.error(f"Transaction update failed with status: {status['status']}")
616
- # return {"error": "An error has occurred while updating the transaction"}
617
- # except Exception as e:
618
- # logging.error(f"An error occurred: {e}")
619
- # # Move the image file to the error folder
620
- # await move_file_to_folder(image_id, parent_folders, user_data['gDrive_metadata']['error'])
621
- # return {"error": str(e)}
622
  async def create_shortcut(file_id, parent_folder_id, name):
623
  try:
624
  file_metadata = {
@@ -709,113 +564,182 @@ async def update_user_sheets_and_folders(user_data, transaction_ref, transaction
709
 
710
  @app.post("/process-image")
711
  async def process_photo(background_tasks: BackgroundTasks, image_id: str = Form(...), user_id: str = Form(...)):
712
- transaction_ref = transactions_collection_ref.document(user_id)
713
- transaction = transaction_ref.get()
714
-
715
- if not transaction.exists:
716
- transaction_ref.set({
717
- "no_sms": 0,
718
- "no_contacts": 0,
719
- "no_receipts": 0,
720
- "no_business_cards": 0,
721
- "purchased_credit": {
722
- "image_detection": 100,
723
- "sync_data": 500,
724
- }
725
- })
726
- transaction = transaction_ref.get()
727
-
728
- transaction_data = transaction.to_dict()
729
-
730
- if transaction_data['no_receipts'] + transaction_data['no_business_cards'] >= transaction_data['purchased_credit']['image_detection']:
731
- return {"error": "You have reached the limit of the number of items you can process. Please upgrade your plan."}
732
-
733
- user_ref = users_collection_ref.document(user_id)
734
- user = user_ref.get()
735
- user_data = user.to_dict()
736
-
737
- background_tasks.add_task(update_user_sheets_and_folders, user_data, transaction_ref, transaction_data, image_id)
738
- return {"success": True}
739
-
740
- @app.post("/sync-data")
741
- async def process_photo(data_json: str = Form(...), user_id: str = Form(...)):
742
- # print(data_json)
743
- data: Dict[str, Any] = json.loads(data_json)
744
- transaction_ref = transactions_collection_ref.document(user_id)
745
- transaction = transaction_ref.get()
746
-
747
- if not transaction.exists:
748
- transaction_ref.set({
749
- "no_sms": 0,
750
- "no_contacts": 0,
751
- "no_receipts": 0,
752
- "no_business_cards": 0,
753
- "purchased_credit": {
754
- "image_detection": 100,
755
- "sync_data": 500,
756
- }
757
- })
758
  transaction = transaction_ref.get()
759
 
760
- transaction_data = transaction.to_dict()
761
-
762
- if transaction_data['no_contacts'] + transaction_data['no_sms'] >= transaction_data['purchased_credit']['sync_data']:
763
- return {"error": "You have reached the limit of the number of items you can process. Please upgrade your plan."}
764
-
765
- user_ref = users_collection_ref.document(user_id)
766
- user = user_ref.get()
767
- user_data = user.to_dict()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768
 
 
 
 
769
  try:
770
- if data['isContacts']:
771
- await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], contacts_ss, "연락처", data['data'], True)
772
- transaction_data['no_contacts'] += len(data['data'])
773
- transaction_ref.update({'no_contacts': transaction_data['no_contacts']})
774
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  else:
776
  if 'sms_ids' not in user_data:
777
- print('No sms_ids')
778
  user_data['sms_ids'] = {}
779
 
780
  existing_phones = set(user_data.get('sms_ids', {}).keys())
781
  new_phones = [phone['address'] for phone in data['data'] if phone['address'] not in existing_phones]
782
-
783
- print(f"Existing phones: {existing_phones}")
784
-
785
  for phone_address in new_phones:
786
- new_sheet_id = await copy_sms_sheet(phone_address, user_data['gDrive_metadata']['spreadsheet'])
787
- print(f"New sheet id: {new_sheet_id}")
788
- user_data['sms_ids'][phone_address] = {'sheet_id': new_sheet_id, 'last_updated': "1970-01-01T00:00:00.000"}
789
- print(user_data['sms_ids'])
790
-
 
 
791
  updated_sms_count = 0
792
  for phone in data['data']:
793
- last_updated = user_data['sms_ids'][phone['address']]['last_updated']
794
- print(f"Last updated: {last_updated}")
795
-
796
- # Convert last_updated to datetime for comparison
797
- last_updated_dt = datetime.fromisoformat(last_updated)
798
- update_message = [sms for sms in phone['sms'] if datetime.fromisoformat(sms[0]) > last_updated_dt]
799
- print(f"Update message: {update_message}")
800
-
801
- if not update_message:
 
 
 
 
 
 
802
  continue
803
-
804
- sheet_id = user_data['sms_ids'][phone['address']]['sheet_id']
805
- print(f"Sheet ID: {sheet_id}")
806
 
807
- await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], sheet_id, 'SMS ' + phone['address'], update_message, False)
808
-
809
- user_data['sms_ids'][phone['address']]['last_updated'] = phone['sms'][0][0]
810
- updated_sms_count += len(update_message)
 
 
 
 
811
 
812
-
813
- user_ref.update({'sms_ids': user_data['sms_ids']})
814
- transaction_data['no_sms'] += updated_sms_count
815
- transaction_ref.update({'no_sms': transaction_data['no_sms']})
816
 
817
-
818
- return {"success": True}
 
819
  except Exception as e:
820
- logging.error(f"An error occurred: {e}")
821
- return {"error": str(e)}
 
209
  logging.error(f"Failed to move file {file_id} to new folders: {e}")
210
 
211
 
212
+ # Update the update_user_sheets_and_folders function for better error handling
213
+ async def update_user_sheets_and_folders(user_data, transaction_ref, transaction_data, image_id):
214
+ try:
215
+ parent_folders = [user_data['gDrive_metadata']['yungsoogi'], user_data['gDrive_metadata']['uploaded']]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ # Request GPT-4 completion
218
+ dataJson = await request_gpt4o_completion(image_id, transaction_data['purchased_credit']['image_detection'])
219
+ if dataJson is None:
220
+ raise Exception("Failed to process the image with GPT-4")
221
+
222
+ data = json.loads(dataJson)
223
+
224
+ # Process receipts and business cards
225
+ found_receipt_no = len(data.get('receipts', []))
226
+ found_business_cards_no = len(data.get('busi_cards', []))
227
+
228
+ new_folder = None
229
+ secondary_folder = None
230
+ timestamp = datetime.now(kst).strftime("%Y-%m-%d %H:%M:%S")
231
+
232
+ # Process receipts
233
+ if found_receipt_no > 0:
234
+ new_folder = user_data['gDrive_metadata']['receipts']
235
+ receipts_data = convert_dicts_to_list(
236
+ data['receipts'],
237
+ receipt_sheet_headers,
238
+ add_link=True,
239
+ image_id=image_id,
240
+ link_text="바로가기",
241
+ link_position=1
242
+ )
243
+ try:
244
+ await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], receipts_ss, "영수증", receipts_data)
245
+ except HttpError as e:
246
+ logging.error(f"Failed to update receipt sheet: {str(e)}")
247
+ raise Exception("Failed to update receipt data in spreadsheet")
248
 
249
+ # Process business cards
250
+ if found_business_cards_no > 0:
251
+ if new_folder is None:
252
+ new_folder = user_data['gDrive_metadata']['business_cards']
 
253
  else:
254
+ secondary_folder = user_data['gDrive_metadata']['business_cards']
255
+ business_cards_data = convert_dicts_to_list(
256
+ data['busi_cards'],
257
+ business_card_sheet_headers,
258
+ add_link=True,
259
+ image_id=image_id,
260
+ link_text="명함보기",
261
+ link_position=-1
262
+ )
263
+ try:
264
+ await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], business_cards_ss, "명함", business_cards_data)
265
+ except HttpError as e:
266
+ logging.error(f"Failed to update business card sheet: {str(e)}")
267
+ raise Exception("Failed to update business card data in spreadsheet")
268
+
269
+ # Update Firebase transaction
270
+ status = await update_fb_transaction(
271
+ transaction_ref,
272
+ no_receipts=found_receipt_no,
273
+ no_business_cards=found_business_cards_no
274
+ )
275
+
276
+ if status['status'] != "Success":
277
+ logging.error(f"Transaction update failed with status: {status['status']}")
278
+ raise Exception("Failed to update transaction in Firebase")
279
+
280
+ # Move file to appropriate folder
281
+ if new_folder:
282
+ try:
283
+ await move_file_to_folder(image_id, parent_folders, [new_folder])
284
+ if secondary_folder:
285
+ await create_shortcut(image_id, secondary_folder, f"Shortcut to {image_id}")
286
+ except HttpError as e:
287
+ logging.error(f"Failed to move file or create shortcut: {str(e)}")
288
+ raise Exception("Failed to organize processed image in Google Drive")
289
+
290
+ logging.info(f"Successfully processed image {image_id} for user {user_data['uid']}")
291
+
292
+ except Exception as e:
293
+ logging.error(f"Error processing image {image_id} for user {user_data['uid']}: {str(e)}")
294
+ try:
295
+ # Move the image file to the error folder
296
+ await move_file_to_folder(image_id, parent_folders, [user_data['gDrive_metadata']['error']])
297
+ logging.info(f"Moved image {image_id} to error folder for user {user_data['uid']}")
298
+ except Exception as move_error:
299
+ logging.error(f"Failed to move image {image_id} to error folder: {str(move_error)}")
300
+
301
+ # You might want to update the transaction or user document to indicate the error
302
+ # This depends on your error handling strategy
303
 
304
  async def copy_sms_sheet(phone_no: str, spreadsheet_id: str):
305
  requests = [
 
383
  async def edit_spreadsheet(target_sheet_id: str = Form(...)):
384
  try:
385
  # Retrieve the target spreadsheet's metadata
386
+ try:
387
+ target_spreadsheet = sheet_service.spreadsheets().get(spreadsheetId=target_sheet_id).execute()
388
+ except HttpError as e:
389
+ if e.resp.status == 404:
390
+ raise HTTPException(status_code=404, detail="Target spreadsheet not found")
391
+ else:
392
+ raise HTTPException(status_code=e.resp.status, detail=f"Error accessing target spreadsheet: {e.error_details}")
393
 
394
  # Collect all sheet IDs in the target spreadsheet
395
  target_sheet_ids = [sheet['properties']['sheetId'] for sheet in target_spreadsheet.get('sheets', [])]
 
404
  })
405
 
406
  # Retrieve the template spreadsheet's metadata
407
+ try:
408
+ template_spreadsheet = sheet_service.spreadsheets().get(spreadsheetId=template_sheet_id).execute()
409
+ except HttpError as e:
410
+ if e.resp.status == 404:
411
+ raise HTTPException(status_code=404, detail="Template spreadsheet not found")
412
+ else:
413
+ raise HTTPException(status_code=e.resp.status, detail=f"Error accessing template spreadsheet: {e.error_details}")
414
 
415
  # Iterate through each sheet in the template spreadsheet
416
  for sheet in template_spreadsheet.get('sheets', []):
417
  sheet_id = sheet['properties']['sheetId']
418
 
419
+ try:
420
+ # Use the copyTo method to copy the sheet to the target spreadsheet
421
+ copy_request = sheet_service.spreadsheets().sheets().copyTo(
422
+ spreadsheetId=template_sheet_id,
423
+ sheetId=sheet_id,
424
+ body={"destinationSpreadsheetId": target_sheet_id}
425
+ )
426
+ copy_response = copy_request.execute()
427
+
428
+ # Get the copied sheet ID
429
+ copied_sheet_id = copy_response['sheetId']
430
+
431
+ # Rename the copied sheet to remove "Copy of"
432
+ copied_sheet_title = sheet['properties']['title'].replace("Copy of ", "")
433
+ rename_request = {
434
+ "updateSheetProperties": {
435
+ "properties": {
436
+ "sheetId": copied_sheet_id,
437
+ "title": copied_sheet_title
438
+ },
439
+ "fields": "title"
440
+ }
441
  }
 
442
 
443
+ # Execute rename request
444
+ sheet_service.spreadsheets().batchUpdate(
445
+ spreadsheetId=target_sheet_id,
446
+ body={"requests": [rename_request]}
447
+ ).execute()
448
 
449
+ logging.info(f"Sheet {sheet['properties']['title']} copied and renamed to {copied_sheet_title} in {target_sheet_id}.")
450
+ except HttpError as e:
451
+ logging.error(f"Error copying or renaming sheet {sheet['properties']['title']}: {e}")
452
+ raise HTTPException(status_code=e.resp.status, detail=f"Error copying or renaming sheet: {e.error_details}")
453
 
454
  if requests:
455
+ try:
456
+ # Execute batch update to delete sheets
457
+ batch_update_request = {"requests": requests}
458
+ sheet_service.spreadsheets().batchUpdate(
459
+ spreadsheetId=target_sheet_id,
460
+ body=batch_update_request
461
+ ).execute()
462
+ logging.info(f"Deleted existing sheets in target spreadsheet {target_sheet_id}.")
463
+ except HttpError as e:
464
+ logging.error(f"Error deleting existing sheets: {e}")
465
+ raise HTTPException(status_code=e.resp.status, detail=f"Error deleting existing sheets: {e.error_details}")
466
 
467
  logging.info(f"All sheets from template {template_sheet_id} copied to {target_sheet_id}.")
468
  return {"message": "Spreadsheet copied successfully"}
469
 
470
+ except HTTPException as he:
471
+ # Re-raise HTTP exceptions
472
+ raise he
473
  except Exception as e:
474
+ logging.error(f"Unexpected error in edit_spreadsheet: {e}")
475
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  async def create_shortcut(file_id, parent_folder_id, name):
478
  try:
479
  file_metadata = {
 
564
 
565
  @app.post("/process-image")
566
  async def process_photo(background_tasks: BackgroundTasks, image_id: str = Form(...), user_id: str = Form(...)):
567
+ try:
568
+ # Validate input
569
+ if not image_id or not user_id:
570
+ raise HTTPException(status_code=400, detail="Both image_id and user_id are required")
571
+
572
+ # Get transaction data
573
+ transaction_ref = transactions_collection_ref.document(user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  transaction = transaction_ref.get()
575
 
576
+ if not transaction.exists:
577
+ # Initialize new transaction if it doesn't exist
578
+ try:
579
+ transaction_ref.set({
580
+ "no_sms": 0,
581
+ "no_contacts": 0,
582
+ "no_receipts": 0,
583
+ "no_business_cards": 0,
584
+ "purchased_credit": {
585
+ "image_detection": 100,
586
+ "sync_data": 500,
587
+ }
588
+ })
589
+ transaction = transaction_ref.get()
590
+ except Exception as e:
591
+ logging.error(f"Failed to initialize new transaction for user {user_id}: {str(e)}")
592
+ raise HTTPException(status_code=500, detail="Failed to initialize user transaction")
593
+
594
+ transaction_data = transaction.to_dict()
595
+
596
+ # Check credit limit
597
+ if transaction_data['no_receipts'] + transaction_data['no_business_cards'] >= transaction_data['purchased_credit']['image_detection']:
598
+ raise HTTPException(status_code=403, detail="You have reached the limit of the number of items you can process. Please upgrade your plan.")
599
+
600
+ # Get user data
601
+ user_ref = users_collection_ref.document(user_id)
602
+ user = user_ref.get()
603
+ if not user.exists:
604
+ raise HTTPException(status_code=404, detail="User not found")
605
+ user_data = user.to_dict()
606
+
607
+ # Validate user data
608
+ if 'gDrive_metadata' not in user_data:
609
+ raise HTTPException(status_code=400, detail="User Google Drive metadata not found")
610
+
611
+ # Add task to background
612
+ try:
613
+ background_tasks.add_task(update_user_sheets_and_folders, user_data, transaction_ref, transaction_data, image_id)
614
+ except Exception as e:
615
+ logging.error(f"Failed to add background task for user {user_id}, image {image_id}: {str(e)}")
616
+ raise HTTPException(status_code=500, detail="Failed to start image processing")
617
+
618
+ return {"success": True, "message": "Image processing started"}
619
+
620
+ except HTTPException as he:
621
+ # Re-raise HTTP exceptions
622
+ raise he
623
+ except Exception as e:
624
+ logging.error(f"Unexpected error in process_photo for user {user_id}, image {image_id}: {str(e)}")
625
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
626
 
627
+
628
+ @app.post("/sync-data")
629
+ async def sync_data(data_json: str = Form(...), user_id: str = Form(...)):
630
  try:
631
+ # Validate input
632
+ if not data_json or not user_id:
633
+ raise HTTPException(status_code=400, detail="Both data_json and user_id are required")
634
+
635
+ # Parse JSON data
636
+ try:
637
+ data: Dict[str, Any] = json.loads(data_json)
638
+ except json.JSONDecodeError as e:
639
+ logging.error(f"Invalid JSON data for user {user_id}: {str(e)}")
640
+ raise HTTPException(status_code=400, detail="Invalid JSON data")
641
+
642
+ # Get transaction data
643
+ transaction_ref = transactions_collection_ref.document(user_id)
644
+ try:
645
+ transaction = transaction_ref.get()
646
+ if not transaction.exists:
647
+ transaction_ref.set({
648
+ "no_sms": 0,
649
+ "no_contacts": 0,
650
+ "no_receipts": 0,
651
+ "no_business_cards": 0,
652
+ "purchased_credit": {
653
+ "image_detection": 100,
654
+ "sync_data": 500,
655
+ }
656
+ })
657
+ transaction = transaction_ref.get()
658
+ except GoogleAPICallError as e:
659
+ logging.error(f"Firebase error for user {user_id}: {str(e)}")
660
+ raise HTTPException(status_code=500, detail="Error accessing user transaction data")
661
+
662
+ transaction_data = transaction.to_dict()
663
+
664
+ # Check credit limit
665
+ if transaction_data['no_contacts'] + transaction_data['no_sms'] >= transaction_data['purchased_credit']['sync_data']:
666
+ raise HTTPException(status_code=403, detail="You have reached the limit of the number of items you can process. Please upgrade your plan.")
667
+
668
+ # Get user data
669
+ user_ref = users_collection_ref.document(user_id)
670
+ try:
671
+ user = user_ref.get()
672
+ if not user.exists:
673
+ raise HTTPException(status_code=404, detail="User not found")
674
+ user_data = user.to_dict()
675
+ except GoogleAPICallError as e:
676
+ logging.error(f"Firebase error fetching user data for user {user_id}: {str(e)}")
677
+ raise HTTPException(status_code=500, detail="Error accessing user data")
678
+
679
+ # Process contacts
680
+ if data.get('isContacts', False):
681
+ try:
682
+ await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], contacts_ss, "연락처", data['data'], True)
683
+ new_contacts_count = len(data['data'])
684
+ transaction_data['no_contacts'] += new_contacts_count
685
+ transaction_ref.update({'no_contacts': transaction_data['no_contacts']})
686
+ logging.info(f"Updated {new_contacts_count} contacts for user {user_id}")
687
+ except HttpError as e:
688
+ logging.error(f"Google Sheets API error updating contacts for user {user_id}: {str(e)}")
689
+ raise HTTPException(status_code=500, detail="Error updating contact data")
690
+ except KeyError as e:
691
+ logging.error(f"Missing key in user data for user {user_id}: {str(e)}")
692
+ raise HTTPException(status_code=500, detail="Error accessing user Google Drive data")
693
+
694
+ # Process SMS
695
  else:
696
  if 'sms_ids' not in user_data:
 
697
  user_data['sms_ids'] = {}
698
 
699
  existing_phones = set(user_data.get('sms_ids', {}).keys())
700
  new_phones = [phone['address'] for phone in data['data'] if phone['address'] not in existing_phones]
701
+
 
 
702
  for phone_address in new_phones:
703
+ try:
704
+ new_sheet_id = await copy_sms_sheet(phone_address, user_data['gDrive_metadata']['spreadsheet'])
705
+ user_data['sms_ids'][phone_address] = {'sheet_id': new_sheet_id, 'last_updated': "1970-01-01T00:00:00.000"}
706
+ except HttpError as e:
707
+ logging.error(f"Google Sheets API error creating new SMS sheet for user {user_id}, phone {phone_address}: {str(e)}")
708
+ raise HTTPException(status_code=500, detail=f"Error creating new SMS sheet for phone {phone_address}")
709
+
710
  updated_sms_count = 0
711
  for phone in data['data']:
712
+ try:
713
+ last_updated = user_data['sms_ids'][phone['address']]['last_updated']
714
+ last_updated_dt = datetime.fromisoformat(last_updated)
715
+ update_message = [sms for sms in phone['sms'] if datetime.fromisoformat(sms[0]) > last_updated_dt]
716
+
717
+ if update_message:
718
+ sheet_id = user_data['sms_ids'][phone['address']]['sheet_id']
719
+ await update_user_sheet(user_data['gDrive_metadata']['spreadsheet'], sheet_id, 'SMS ' + phone['address'], update_message, False)
720
+ user_data['sms_ids'][phone['address']]['last_updated'] = phone['sms'][0][0]
721
+ updated_sms_count += len(update_message)
722
+ except KeyError as e:
723
+ logging.error(f"Missing key in SMS data for user {user_id}, phone {phone['address']}: {str(e)}")
724
+ continue
725
+ except HttpError as e:
726
+ logging.error(f"Google Sheets API error updating SMS for user {user_id}, phone {phone['address']}: {str(e)}")
727
  continue
 
 
 
728
 
729
+ try:
730
+ user_ref.update({'sms_ids': user_data['sms_ids']})
731
+ transaction_data['no_sms'] += updated_sms_count
732
+ transaction_ref.update({'no_sms': transaction_data['no_sms']})
733
+ logging.info(f"Updated {updated_sms_count} SMS messages for user {user_id}")
734
+ except GoogleAPICallError as e:
735
+ logging.error(f"Firebase error updating user data for user {user_id}: {str(e)}")
736
+ raise HTTPException(status_code=500, detail="Error updating user data")
737
 
738
+ return {"success": True, "updated_count": new_contacts_count if data.get('isContacts', False) else updated_sms_count}
 
 
 
739
 
740
+ except HTTPException as he:
741
+ # Re-raise HTTP exceptions
742
+ raise he
743
  except Exception as e:
744
+ logging.error(f"Unexpected error in sync_data for user {user_id}: {str(e)}")
745
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")