AlanRex commited on
Commit
2e431ca
·
verified ·
1 Parent(s): b7c3870

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +217 -2
main.py CHANGED
@@ -7,6 +7,7 @@ from linebot.exceptions import InvalidSignatureError
7
  from linebot.models import MessageEvent, TextMessage, TextSendMessage
8
  import PyPDF2
9
  import logging
 
10
  from datetime import datetime, timedelta
11
  from google.oauth2.credentials import Credentials
12
  from google.oauth2 import service_account
@@ -275,7 +276,7 @@ def parse_date(date_str):
275
  except ValueError:
276
  pass
277
 
278
- return None, "❌ 日期格式不正確,請重新輸入。\n例如:明天、12/26、2025-12-30"
279
 
280
  def validate_time(time_str):
281
  time_keywords = ["點", "時", ":", "-", "到", "至", "~"]
@@ -496,11 +497,186 @@ def root():
496
  "google_calendar": calendar_service is not None,
497
  "ai_qa": ai_enabled,
498
  "pdf_loaded": len(pdf_content) > 0
 
 
 
 
 
 
499
  }
500
  }
501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  @app.get("/bookings")
503
  def get_bookings():
 
504
  bookings_file = pathlib.Path("bookings/bookings.json")
505
  if bookings_file.exists():
506
  with open(bookings_file, 'r', encoding='utf-8') as f:
@@ -508,6 +684,45 @@ def get_bookings():
508
  return {"total": len(bookings), "bookings": bookings}
509
  return {"total": 0, "bookings": []}
510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  @app.post("/webhook")
512
  async def webhook(
513
  request: Request,
@@ -573,7 +788,7 @@ def handle_message(event):
573
  full_prompt = user_message
574
 
575
  response = client.models.generate_content(
576
- model="gemini-1.5-flash",
577
  contents=full_prompt
578
  )
579
 
 
7
  from linebot.models import MessageEvent, TextMessage, TextSendMessage
8
  import PyPDF2
9
  import logging
10
+ from fastapi.responses import HTMLResponse
11
  from datetime import datetime, timedelta
12
  from google.oauth2.credentials import Credentials
13
  from google.oauth2 import service_account
 
276
  except ValueError:
277
  pass
278
 
279
+ return None, "❌ 日期格式不正確,請重新輸入。\n例如:明天、2025-12-30"
280
 
281
  def validate_time(time_str):
282
  time_keywords = ["點", "時", ":", "-", "到", "至", "~"]
 
497
  "google_calendar": calendar_service is not None,
498
  "ai_qa": ai_enabled,
499
  "pdf_loaded": len(pdf_content) > 0
500
+ },
501
+ "endpoints": {
502
+ "查看所有預約": "/bookings",
503
+ "下載預約資料": "/bookings/download",
504
+ "查看最新預約": "/bookings/latest",
505
+ "管理介面": "/admin"
506
  }
507
  }
508
 
509
+ @app.get("/admin")
510
+ def admin_panel():
511
+ """簡單的管理介面"""
512
+ bookings_file = pathlib.Path("bookings/bookings.json")
513
+ bookings = []
514
+
515
+ if bookings_file.exists():
516
+ with open(bookings_file, 'r', encoding='utf-8') as f:
517
+ bookings = json.load(f)
518
+
519
+ # 生成 HTML
520
+ html = """
521
+ <!DOCTYPE html>
522
+ <html>
523
+ <head>
524
+ <meta charset="UTF-8">
525
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
526
+ <title>琴房預約管理</title>
527
+ <style>
528
+ body {
529
+ font-family: Arial, sans-serif;
530
+ max-width: 1200px;
531
+ margin: 0 auto;
532
+ padding: 20px;
533
+ background: #f5f5f5;
534
+ }
535
+ h1 {
536
+ color: #333;
537
+ text-align: center;
538
+ }
539
+ .stats {
540
+ display: flex;
541
+ gap: 20px;
542
+ margin-bottom: 30px;
543
+ }
544
+ .stat-card {
545
+ flex: 1;
546
+ background: white;
547
+ padding: 20px;
548
+ border-radius: 8px;
549
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
550
+ }
551
+ .stat-card h3 {
552
+ margin: 0 0 10px 0;
553
+ color: #666;
554
+ font-size: 14px;
555
+ }
556
+ .stat-card .number {
557
+ font-size: 32px;
558
+ font-weight: bold;
559
+ color: #4CAF50;
560
+ }
561
+ table {
562
+ width: 100%;
563
+ background: white;
564
+ border-collapse: collapse;
565
+ border-radius: 8px;
566
+ overflow: hidden;
567
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
568
+ }
569
+ th {
570
+ background: #4CAF50;
571
+ color: white;
572
+ padding: 12px;
573
+ text-align: left;
574
+ }
575
+ td {
576
+ padding: 12px;
577
+ border-bottom: 1px solid #ddd;
578
+ }
579
+ tr:hover {
580
+ background: #f9f9f9;
581
+ }
582
+ .status-pending {
583
+ color: #FF9800;
584
+ font-weight: bold;
585
+ }
586
+ .calendar-link {
587
+ color: #2196F3;
588
+ text-decoration: none;
589
+ }
590
+ .calendar-link:hover {
591
+ text-decoration: underline;
592
+ }
593
+ .refresh-btn {
594
+ background: #4CAF50;
595
+ color: white;
596
+ padding: 10px 20px;
597
+ border: none;
598
+ border-radius: 4px;
599
+ cursor: pointer;
600
+ font-size: 16px;
601
+ margin-bottom: 20px;
602
+ }
603
+ .refresh-btn:hover {
604
+ background: #45a049;
605
+ }
606
+ </style>
607
+ </head>
608
+ <body>
609
+ <h1>🎹 琴房預約管理系統</h1>
610
+
611
+ <div class="stats">
612
+ <div class="stat-card">
613
+ <h3>總預約數</h3>
614
+ <div class="number">""" + str(len(bookings)) + """</div>
615
+ </div>
616
+ <div class="stat-card">
617
+ <h3>待確認</h3>
618
+ <div class="number">""" + str(len([b for b in bookings if b.get('status') == 'pending'])) + """</div>
619
+ </div>
620
+ <div class="stat-card">
621
+ <h3>Google Calendar</h3>
622
+ <div class="number">""" + ('✓' if calendar_service else '✗') + """</div>
623
+ </div>
624
+ </div>
625
+
626
+ <button class="refresh-btn" onclick="location.reload()">🔄 重新整理</button>
627
+
628
+ <table>
629
+ <thead>
630
+ <tr>
631
+ <th>預約編號</th>
632
+ <th>日期</th>
633
+ <th>時段</th>
634
+ <th>人數</th>
635
+ <th>琴房</th>
636
+ <th>狀態</th>
637
+ <th>建立時間</th>
638
+ <th>日曆</th>
639
+ </tr>
640
+ </thead>
641
+ <tbody>
642
+ """
643
+
644
+ # 按時間倒序排列
645
+ for booking in reversed(bookings):
646
+ calendar_icon = '📆' if booking.get('calendar_link') else '❌'
647
+ html += f"""
648
+ <tr>
649
+ <td>{booking['booking_id']}</td>
650
+ <td>{booking['date']}</td>
651
+ <td>{booking['time']}</td>
652
+ <td>{booking['people']}人</td>
653
+ <td>{booking['room']}</td>
654
+ <td class="status-pending">{booking['status']}</td>
655
+ <td>{booking['created_at']}</td>
656
+ <td>
657
+ """
658
+ if booking.get('calendar_link'):
659
+ html += f'<a href="{booking["calendar_link"]}" target="_blank" class="calendar-link">{calendar_icon} 查看</a>'
660
+ else:
661
+ html += calendar_icon
662
+ html += """
663
+ </td>
664
+ </tr>
665
+ """
666
+
667
+ html += """
668
+ </tbody>
669
+ </table>
670
+ </body>
671
+ </html>
672
+ """
673
+
674
+ from fastapi.responses import HTMLResponse
675
+ return HTMLResponse(content=html)
676
+
677
  @app.get("/bookings")
678
  def get_bookings():
679
+ """查看所有預約(管理用)"""
680
  bookings_file = pathlib.Path("bookings/bookings.json")
681
  if bookings_file.exists():
682
  with open(bookings_file, 'r', encoding='utf-8') as f:
 
684
  return {"total": len(bookings), "bookings": bookings}
685
  return {"total": 0, "bookings": []}
686
 
687
+ @app.get("/bookings/download")
688
+ def download_bookings():
689
+ """下載預約資料(JSON 格式)"""
690
+ bookings_file = pathlib.Path("bookings/bookings.json")
691
+ if bookings_file.exists():
692
+ with open(bookings_file, 'r', encoding='utf-8') as f:
693
+ return json.load(f)
694
+ return []
695
+
696
+ @app.get("/bookings/latest")
697
+ def get_latest_booking():
698
+ """查看最新一筆預約"""
699
+ bookings_file = pathlib.Path("bookings/bookings.json")
700
+ if bookings_file.exists():
701
+ with open(bookings_file, 'r', encoding='utf-8') as f:
702
+ bookings = json.load(f)
703
+ if bookings:
704
+ return bookings[-1] # 返回最後一筆
705
+ return {"message": "尚無預約資料"}
706
+
707
+ @app.delete("/bookings/{booking_id}")
708
+ def delete_booking(booking_id: str):
709
+ """刪除指定預約"""
710
+ bookings_file = pathlib.Path("bookings/bookings.json")
711
+ if bookings_file.exists():
712
+ with open(bookings_file, 'r', encoding='utf-8') as f:
713
+ bookings = json.load(f)
714
+
715
+ # 找到並移除指定預約
716
+ updated_bookings = [b for b in bookings if b['booking_id'] != booking_id]
717
+
718
+ if len(updated_bookings) < len(bookings):
719
+ with open(bookings_file, 'w', encoding='utf-8') as f:
720
+ json.dump(updated_bookings, f, ensure_ascii=False, indent=2)
721
+ return {"message": f"已刪除預約 {booking_id}"}
722
+ else:
723
+ return {"message": f"找不到預約 {booking_id}"}
724
+ return {"message": "沒有預約資料"}
725
+
726
  @app.post("/webhook")
727
  async def webhook(
728
  request: Request,
 
788
  full_prompt = user_message
789
 
790
  response = client.models.generate_content(
791
+ model="gemini-2.5-flash", # 使用最新版本
792
  contents=full_prompt
793
  )
794