Spaces:
Runtime error
Runtime error
import gradio as gr | |
import sqlite3 | |
import json | |
from huggingface_hub import hf_hub_download, upload_file | |
import os | |
from pathlib import Path | |
from reportlab.lib.pagesizes import A4 | |
from reportlab.lib.units import cm | |
from reportlab.pdfgen import canvas | |
from reportlab.pdfbase import pdfmetrics | |
from reportlab.pdfbase.ttfonts import TTFont | |
import qrcode | |
import zipfile | |
import io | |
from dotenv import load_dotenv | |
import time | |
import requests | |
# อ่าน .env (ถ้ามี) | |
load_dotenv() | |
REPO_ID = os.getenv("REPO_ID", "protae5544/WorkerManagement") | |
ORIGINAL_REPO_ID = os.getenv("ORIGINAL_REPO_ID", None) | |
# ดาวน์โหลดฟอนต์จาก Space อื่น | |
def download_fonts(): | |
try: | |
# ดาวน์โหลดฟอนต์จาก protae5544/worberkit2024poe | |
font_urls = { | |
'THSarabunNew.ttf': 'https://huggingface.co/spaces/protae5544/worberkit2024poe/resolve/main/fontCSS/subset-THSarabunNew.woff2', | |
'THSarabunNew-Bold.ttf': 'https://huggingface.co/spaces/protae5544/worberkit2024poe/resolve/main/fontCSS/subset-THSarabunNew-Bold.woff2' | |
} | |
# ใช้ฟอนต์ที่มีอยู่แล้วในระบบ หรือดาวน์โหลดจาก Google Fonts | |
google_fonts = { | |
'THSarabunNew.ttf': 'https://fonts.gstatic.com/s/sarabun/v13/DtVjJx26TKEqsc-lWcmw-wKE7wM.ttf', | |
'THSarabunNew-Bold.ttf': 'https://fonts.gstatic.com/s/sarabun/v13/DtVmJx26TKEqsc-lWcmw5wq0hwOKBwXcb0A.ttf' | |
} | |
for filename, url in google_fonts.items(): | |
if not os.path.exists(filename): | |
try: | |
response = requests.get(url, timeout=10) | |
if response.status_code == 200: | |
with open(filename, 'wb') as f: | |
f.write(response.content) | |
print(f"Downloaded font: {filename}") | |
except Exception as e: | |
print(f"Error downloading {filename}: {e}") | |
except Exception as e: | |
print(f"Error in download_fonts: {e}") | |
# ตั้งค่าฟอนต์ | |
def setup_fonts(): | |
try: | |
# ดาวน์โหลดฟอนต์ก่อน | |
download_fonts() | |
# ลงทะเบียนฟอนต์ | |
if os.path.exists('THSarabunNew.ttf'): | |
pdfmetrics.registerFont(TTFont('THSarabun', 'THSarabunNew.ttf')) | |
print("Registered THSarabun font") | |
if os.path.exists('THSarabunNew-Bold.ttf'): | |
pdfmetrics.registerFont(TTFont('THSarabunBold', 'THSarabunNew-Bold.ttf')) | |
print("Registered THSarabunBold font") | |
return True | |
except Exception as e: | |
print(f"Warning: Could not load fonts: {e}") | |
return False | |
# เริ่มต้นฟอนต์ | |
font_available = setup_fonts() | |
# ฟังก์ชันแปลงวันที่เป็นภาษาไทย | |
def thai_date_time(timestamp): | |
months = { | |
1: "มกราคม", 2: "กุมภาพันธ์", 3: "มีนาคม", 4: "เมษายน", | |
5: "พฤษภาคม", 6: "มิถุนายน", 7: "กรกฎาคม", 8: "สิงหาคม", | |
9: "กันยายน", 10: "ตุลาคม", 11: "พฤศจิกายน", 12: "ธันวาคม" | |
} | |
dt = time.localtime(timestamp) | |
day = dt.tm_mday | |
month = months[dt.tm_mon] | |
year = dt.tm_year + 543 | |
hour = dt.tm_hour | |
minute = dt.tm_min | |
return f"{day:02d} {month} {year} {hour:02d}:{minute:02d} น." | |
# เริ่มต้นฐานข้อมูล | |
def init_db(): | |
conn = sqlite3.connect("workers.db") | |
c = conn.cursor() | |
c.execute('''CREATE TABLE IF NOT EXISTS workers ( | |
request_number TEXT PRIMARY KEY, | |
english_name TEXT, | |
foreign_reference_number TEXT, | |
id_number TEXT, | |
nationality TEXT, | |
receipt_number TEXT, | |
payment_number TEXT, | |
created_at TEXT | |
)''') | |
c.execute('''CREATE TABLE IF NOT EXISTS attachments ( | |
request_number TEXT, | |
file_url TEXT, | |
FOREIGN KEY(request_number) REFERENCES workers(request_number) | |
)''') | |
c.execute('''CREATE TABLE IF NOT EXISTS pdfs ( | |
request_number TEXT, | |
pdf_url TEXT, | |
FOREIGN KEY(request_number) REFERENCES workers(request_number) | |
)''') | |
conn.commit() | |
conn.close() | |
# สำรองฐานข้อมูล | |
def create_receipt_pdf(request_number, english_name, foreign_reference_number, id_number, nationality, receipt_number, payment_number): | |
filename = f"pdfs/receipt_{request_number}.pdf" | |
local_path = f"temp_{request_number}.pdf" | |
# สร้างโฟลเดอร์ pdfs ถ้าไม่มี | |
os.makedirs("pdfs", exist_ok=True) | |
c = canvas.Canvas(local_path, pagesize=A4) | |
width, height = A4 | |
# กำหนดตำแหน่งและขนาดตามเอกสารต้นฉบับ | |
# ใช้ Helvetica สำหรับความเข้ากันได้ | |
# หัวเอกสาร - กระทรวงแรงงาน | |
c.setFont("Helvetica-Bold", 20) | |
c.drawCentredText(width/2, height-80, "กระทรวงแรงงาน") | |
c.setFont("Helvetica-Bold", 16) | |
c.drawCentredText(width/2, height-110, "ใบเสร็จรับเงิน (ต้นฉบับ)") | |
# ข้อมูลส่วนหัว | |
c.setFont("Helvetica", 12) | |
y_pos = height - 150 | |
c.drawString(50, y_pos, f"เลขที่: {receipt_number}") | |
c.drawString(350, y_pos, "แบบ ภ.1600-034") | |
y_pos -= 25 | |
c.drawString(50, y_pos, "ที่ทำการ: สำนักบริหารแรงงานต่างด้าว") | |
y_pos -= 25 | |
c.drawString(50, y_pos, "วันที่: 01 เมษายน 2568") | |
y_pos -= 25 | |
c.drawString(50, y_pos, f"เลขที่ใบชำระเงิน: {payment_number}") | |
y_pos -= 25 | |
c.drawString(50, y_pos, f"เลขรับคำขอที่: {request_number}") | |
# ข้อมูลผู้ชำระเงิน | |
y_pos -= 40 | |
c.setFont("Helvetica-Bold", 12) | |
c.drawString(50, y_pos, "ข้อมูลผู้ชำระเงิน:") | |
y_pos -= 25 | |
c.setFont("Helvetica", 12) | |
c.drawString(50, y_pos, f"ชื่อผู้ชำระเงิน: {english_name}") | |
y_pos -= 20 | |
c.drawString(50, y_pos, f"สัญชาติ: {nationality}") | |
y_pos -= 20 | |
c.drawString(50, y_pos, f"เลขอ้างอิงคนต่างด้าว: {foreign_reference_number}") | |
y_pos -= 20 | |
c.drawString(50, y_pos, f"หมายเลขประจำตัวคนต่างด้าว: {id_number}") | |
# ข้อมูลนายจ้าง | |
y_pos -= 40 | |
c.setFont("Helvetica-Bold", 12) | |
c.drawString(50, y_pos, "ข้อมูลนายจ้าง:") | |
y_pos -= 25 | |
c.setFont("Helvetica", 12) | |
c.drawString(50, y_pos, "ชื่อนายจ้าง/สถานประกอบการ: บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด") | |
y_pos -= 20 | |
c.drawString(50, y_pos, "เลขประจำตัวนายจ้าง: 0415567000061") | |
# รายการค่าธรรมเนียม | |
y_pos -= 40 | |
c.setFont("Helvetica-Bold", 14) | |
c.drawString(50, y_pos, "รายการค่าธรรมเนียม") | |
# วาดเส้นใต้หัวข้อ | |
c.line(50, y_pos-5, 500, y_pos-5) | |
y_pos -= 30 | |
c.setFont("Helvetica", 12) | |
c.drawString(70, y_pos, "1. ค่าธรรมเนียมในการยื่นคำขอ ฉบับละ 100 บาท") | |
c.drawString(450, y_pos, "100.00") | |
y_pos -= 25 | |
c.drawString(70, y_pos, "2. ค่าธรรมเนียมการพิจารณา") | |
c.drawString(450, y_pos, "900.00") | |
# เส้นคั่น | |
y_pos -= 15 | |
c.line(400, y_pos, 500, y_pos) | |
y_pos -= 25 | |
c.setFont("Helvetica-Bold", 12) | |
c.drawString(70, y_pos, "รวมเป็นเงินทั้งสิ้น (บาท): (หนึ่งพันบาทถ้วน)") | |
c.drawString(430, y_pos, "1,000.00") | |
# เส้นใต้ยอดรวม | |
c.line(400, y_pos-5, 500, y_pos-5) | |
c.line(400, y_pos-8, 500, y_pos-8) | |
# ข้อความรับรอง | |
y_pos -= 40 | |
c.setFont("Helvetica", 12) | |
c.drawString(50, y_pos, "ได้รับเงินไว้เป็นการถูกต้องแล้ว") | |
# ลายเซ็น | |
y_pos -= 60 | |
c.drawString(300, y_pos, "(ลงชื่อ) นางสาวอารีวรรณ โพธิ์นิ่มแดง") | |
c.drawString(350, y_pos-20, "(ผู้รับเงิน)") | |
y_pos -= 45 | |
c.drawString(300, y_pos, "ตำแหน่ง: นักวิชาการแรงงานชำนาญการ") | |
# QR Code ที่มุมขวาล่าง | |
pdf_url = f"https://huggingface.co/spaces/{REPO_ID}/resolve/main/{filename}" | |
try: | |
qr = qrcode.QRCode(version=1, box_size=8, border=2) | |
qr.add_data(pdf_url) | |
qr.make(fit=True) | |
qr_img = qr.make_image(fill_color="black", back_color="white") | |
qr_img.save("temp_qr.png") | |
c.drawImage("temp_qr.png", width-150, 50, width=80, height=80) | |
except Exception as e: | |
print(f"Error creating QR code: {e}") | |
# Timestamp ที่ด้านล่าง | |
c.setFont("Helvetica", 8) | |
print_time = thai_date_time(time.time()) | |
c.drawString(50, 30, f"พิมพ์เมื่อ: {print_time}") | |
# เส้นขอบกรอบเอกสาร | |
c.rect(30, 30, width-60, height-60, stroke=1, fill=0) | |
c.showPage() | |
c.save() | |
# เพิ่ม HTML version สำหรับ preview และ print | |
create_html_receipt(request_number, english_name, foreign_reference_number, | |
id_number, nationality, receipt_number, payment_number) | |
# อัพโหลด PDF | |
try: | |
upload_file( | |
path_or_fileobj=local_path, | |
path_in_repo=filename, | |
repo_id=REPO_ID, | |
repo_type="space" | |
) | |
print(f"PDF uploaded successfully: {filename}") | |
except Exception as e: | |
print(f"Error uploading PDF: {e}") | |
return None | |
# บันทึกข้อมูล PDF ในฐานข้อมูล | |
conn = sqlite3.connect("workers.db") | |
cursor = conn.cursor() | |
cursor.execute("INSERT OR REPLACE INTO pdfs (request_number, pdf_url) VALUES (?, ?)", | |
(request_number, pdf_url)) | |
conn.commit() | |
conn.close() | |
return pdf_url | |
# เพิ่มฟังก์ชันสร้าง HTML version | |
def create_html_receipt(request_number, english_name, foreign_reference_number, id_number, nationality, receipt_number, payment_number): | |
html_filename = f"receipts/receipt_{request_number}.html" | |
local_html_path = f"temp_{request_number}.html" | |
os.makedirs("receipts", exist_ok=True) | |
pdf_url = f"https://huggingface.co/spaces/{REPO_ID}/resolve/main/pdfs/receipt_{request_number}.pdf" | |
print_time = thai_date_time(time.time()) | |
html_content = f""" | |
<!DOCTYPE html> | |
<html lang="th"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>ใบเสร็จรับเงิน - {request_number}</title> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/qrcode.min.js"></script> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Sarabun:wght@400;600;700&display=swap'); | |
* {{ | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
}} | |
body {{ | |
font-family: 'Sarabun', Arial, sans-serif; | |
background: #f5f5f5; | |
padding: 20px; | |
}} | |
.receipt-container {{ | |
max-width: 800px; | |
margin: 0 auto; | |
background: white; | |
border: 2px solid #000; | |
padding: 30px; | |
box-shadow: 0 0 20px rgba(0,0,0,0.1); | |
}} | |
.header {{ | |
text-align: center; | |
margin-bottom: 30px; | |
border-bottom: 2px solid #000; | |
padding-bottom: 20px; | |
}} | |
.header h1 {{ | |
font-size: 24px; | |
font-weight: 700; | |
margin-bottom: 10px; | |
}} | |
.header h2 {{ | |
font-size: 18px; | |
font-weight: 600; | |
}} | |
.info-section {{ | |
margin-bottom: 25px; | |
}} | |
.info-row {{ | |
display: flex; | |
justify-content: space-between; | |
margin-bottom: 8px; | |
padding: 5px 0; | |
}} | |
.info-label {{ | |
font-weight: 600; | |
}} | |
.section-title {{ | |
font-size: 16px; | |
font-weight: 700; | |
margin: 20px 0 15px 0; | |
border-bottom: 1px solid #ccc; | |
padding-bottom: 5px; | |
}} | |
.fee-table {{ | |
width: 100%; | |
border-collapse: collapse; | |
margin: 20px 0; | |
}} | |
.fee-table th, | |
.fee-table td {{ | |
border: 1px solid #000; | |
padding: 10px; | |
text-align: left; | |
}} | |
.fee-table th {{ | |
background: #f0f0f0; | |
font-weight: 700; | |
}} | |
.amount {{ | |
text-align: right; | |
font-weight: 600; | |
}} | |
.total-row {{ | |
background: #f9f9f9; | |
font-weight: 700; | |
}} | |
.signature-section {{ | |
margin-top: 40px; | |
display: flex; | |
justify-content: space-between; | |
align-items: flex-end; | |
}} | |
.signature-box {{ | |
text-align: center; | |
}} | |
.qr-section {{ | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
}} | |
.footer {{ | |
margin-top: 30px; | |
text-align: center; | |
font-size: 12px; | |
color: #666; | |
border-top: 1px solid #ccc; | |
padding-top: 15px; | |
}} | |
.download-section {{ | |
text-align: center; | |
margin: 20px 0; | |
padding: 15px; | |
background: #e3f2fd; | |
border-radius: 8px; | |
border: 1px solid #2196f3; | |
}} | |
.download-btn {{ | |
background: #2196f3; | |
color: white; | |
padding: 10px 20px; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
font-size: 16px; | |
margin: 5px; | |
text-decoration: none; | |
display: inline-block; | |
}} | |
.download-btn:hover {{ | |
background: #1976d2; | |
}} | |
/* Print Styles */ | |
@media print {{ | |
body {{ | |
background: white; | |
padding: 0; | |
}} | |
.receipt-container {{ | |
box-shadow: none; | |
border: 2px solid #000; | |
max-width: none; | |
width: 100%; | |
}} | |
.download-section {{ | |
display: none; | |
}} | |
.no-print {{ | |
display: none; | |
}} | |
/* ปรับขนาดสำหรับการพิมพ์ */ | |
.header h1 {{ | |
font-size: 22px; | |
}} | |
.header h2 {{ | |
font-size: 16px; | |
}} | |
.info-row {{ | |
font-size: 14px; | |
}} | |
.fee-table {{ | |
font-size: 14px; | |
}} | |
}} | |
@page {{ | |
margin: 1cm; | |
size: A4; | |
}} | |
</style> | |
</head> | |
<body> | |
<div class="receipt-container"> | |
<!-- Header --> | |
<div class="header"> | |
<h1>กระทรวงแรงงาน</h1> | |
<h2>ใบเสร็จรับเงิน (ต้นฉบับ)</h2> | |
</div> | |
<!-- Download Section (ไม่แสดงเวลาพิมพ์) --> | |
<div class="download-section no-print"> | |
<h3>🔗 ดาวน์โหลดเอกสาร</h3> | |
<a href="{pdf_url}" class="download-btn" target="_blank">📄 ดาวน์โหลด PDF</a> | |
<button onclick="window.print()" class="download-btn">🖨️ พิมพ์เอกสาร</button> | |
</div> | |
<!-- Receipt Info --> | |
<div class="info-section"> | |
<div class="info-row"> | |
<span class="info-label">เลขที่:</span> | |
<span>{receipt_number}</span> | |
<span class="info-label">แบบ ภ.1600-034</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">ที่ทำการ:</span> | |
<span>สำนักบริหารแรงงานต่างด้าว</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">วันที่:</span> | |
<span>01 เมษายน 2568</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">เลขที่ใบชำระเงิน:</span> | |
<span>{payment_number}</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">เลขรับคำขอที่:</span> | |
<span>{request_number}</span> | |
</div> | |
</div> | |
<!-- Payer Info --> | |
<div class="section-title">ข้อมูลผู้ชำระเงิน</div> | |
<div class="info-section"> | |
<div class="info-row"> | |
<span class="info-label">ชื่อผู้ชำระเงิน:</span> | |
<span>{english_name}</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">สัญชาติ:</span> | |
<span>{nationality}</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">เลขอ้างอิงคนต่างด้าว:</span> | |
<span>{foreign_reference_number}</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">หมายเลขประจำตัวคนต่างด้าว:</span> | |
<span>{id_number}</span> | |
</div> | |
</div> | |
<!-- Employer Info --> | |
<div class="section-title">ข้อมูลนายจ้าง</div> | |
<div class="info-section"> | |
<div class="info-row"> | |
<span class="info-label">ชื่อนายจ้าง/สถานประกอบการ:</span> | |
<span>บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</span> | |
</div> | |
<div class="info-row"> | |
<span class="info-label">เลขประจำตัวนายจ้าง:</span> | |
<span>0415567000061</span> | |
</div> | |
</div> | |
<!-- Fee Table --> | |
<div class="section-title">รายการค่าธรรมเนียม</div> | |
<table class="fee-table"> | |
<thead> | |
<tr> | |
<th>รายการ</th> | |
<th class="amount">จำนวนเงิน (บาท)</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr> | |
<td>1. ค่าธรรมเนียมในการยื่นคำขอ ฉบับละ 100 บาท</td> | |
<td class="amount">100.00</td> | |
</tr> | |
<tr> | |
<td>2. ค่าธรรมเนียมการพิจารณา</td> | |
<td class="amount">900.00</td> | |
</tr> | |
<tr class="total-row"> | |
<td><strong>รวมเป็นเงินทั้งสิ้น (บาท): (หนึ่งพันบาทถ้วน)</strong></td> | |
<td class="amount"><strong>1,000.00</strong></td> | |
</tr> | |
</tbody> | |
</table> | |
<div style="margin: 20px 0; font-weight: 600;"> | |
ได้รับเงินไว้เป็นการถูกต้องแล้ว | |
</div> | |
<!-- Signature Section --> | |
<div class="signature-section"> | |
<div class="signature-box"> | |
<div style="margin-bottom: 50px;">_______________________</div> | |
<div>(ลงชื่อ) นางสาวอารีวรรณ โพธิ์นิ่มแดง</div> | |
<div>(ผู้รับเงิน)</div> | |
<div style="margin-top: 10px;">ตำแหน่ง: นักวิชาการแรงงานชำนาญการ</div> | |
</div> | |
<div class="qr-section"> | |
<div id="qrcode"></div> | |
<div style="font-size: 12px; margin-top: 5px;">สแกนเพื่อตรวจสอบ</div> | |
</div> | |
</div> | |
<!-- Footer --> | |
<div class="footer"> | |
<div>พิมพ์เมื่อ: {print_time}</div> | |
<div style="margin-top: 5px;">เอกสารนี้ออกโดยระบบอัตโนมัติ - บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</div> | |
</div> | |
</div> | |
<script> | |
// สร้าง QR Code | |
QRCode.toCanvas(document.getElementById('qrcode'), '{pdf_url}', {{ | |
width: 80, | |
height: 80, | |
margin: 1, | |
color: {{ | |
dark: '#000000', | |
light: '#FFFFFF' | |
}} | |
}}, function (error) {{ | |
if (error) console.error(error); | |
console.log('QR Code generated successfully!'); | |
}}); | |
// Auto-focus สำหรับการพิมพ์ | |
window.addEventListener('load', function() {{ | |
console.log('Receipt loaded successfully'); | |
}}); | |
</script> | |
</body> | |
</html> | |
""" | |
# บันทึกไฟล์ HTML | |
with open(local_html_path, 'w', encoding='utf-8') as f: | |
f.write(html_content) | |
# อัพโหลด HTML | |
try: | |
upload_file( | |
path_or_fileobj=local_html_path, | |
path_in_repo=html_filename, | |
repo_id=REPO_ID, | |
repo_type="space" | |
) | |
print(f"HTML uploaded successfully: {html_filename}") | |
except Exception as e: | |
print(f"Error uploading HTML: {e}") |