Spaces:
Sleeping
Sleeping
| import asyncio | |
| import os | |
| import re | |
| import fitz # PyMuPDF | |
| from flask import Flask, request, render_template_string, send_file | |
| from playwright.async_api import async_playwright | |
| app = Flask(__name__) | |
| # --- UI Design --- | |
| HTML_PAGE = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>JNVU Admit Card Downloader</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <style> | |
| body { font-family: 'Segoe UI', Arial, sans-serif; background: #f0f2f5; display: flex; justify-content: center; padding-top: 50px; } | |
| .card { background: white; padding: 30px; border-radius: 15px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); width: 100%; max-width: 450px; text-align: center; } | |
| h2 { color: #1a73e8; margin-bottom: 20px; } | |
| input { width: 90%; padding: 12px; margin: 10px 0; border: 2px solid #dfe1e5; border-radius: 8px; font-size: 16px; outline: none; } | |
| input:focus { border-color: #1a73e8; } | |
| .btn-fetch { background: #1a73e8; color: white; border: none; padding: 12px 25px; border-radius: 8px; cursor: pointer; font-size: 16px; width: 95%; font-weight: bold; } | |
| .btn-fetch:hover { background: #1557b0; } | |
| .result-box { margin-top: 25px; padding: 20px; background: #f8f9fa; border-radius: 10px; text-align: left; border-left: 5px solid #34a853; } | |
| .btn-download { display: block; text-align: center; background: #34a853; color: white; padding: 12px; text-decoration: none; border-radius: 8px; margin-top: 15px; font-weight: bold; } | |
| .error { color: #d93025; background: #fce8e6; padding: 10px; border-radius: 5px; margin-top: 10px; } | |
| .loading { color: #5f6368; font-style: italic; margin-top: 10px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="card"> | |
| <h2>JNVU Admit Card Finder</h2> | |
| <form method="POST"> | |
| <input type="text" name="form_number" placeholder="Enter Form Number (e.g. 128xxx)" required> | |
| <button type="submit" class="btn-fetch">Fetch Admit Card</button> | |
| </form> | |
| {% if error %}<div class="error">{{ error }}</div>{% endif %} | |
| {% if data %} | |
| <div class="result-box"> | |
| <p><strong>👤 Name:</strong> {{ data.name }}</p> | |
| <p><strong>🔢 Roll No:</strong> {{ data.roll }}</p> | |
| <p><strong>🏫 Center:</strong> {{ data.center }}</p> | |
| <a href="/download/{{ filename }}" class="btn-download">📥 Download PDF Now</a> | |
| </div> | |
| {% endif %} | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| # --- PDF Data Extraction --- | |
| def extract_student_info(pdf_path): | |
| info = {"name": "Not Found", "roll": "Not Found", "center": "Not Found"} | |
| try: | |
| doc = fitz.open(pdf_path) | |
| text = "".join([page.get_text() for page in doc]) | |
| name_m = re.search(r"NAME OF CANDIDATE\s*:\s*(.*)", text) | |
| roll_m = re.search(r"Roll no is\s+([\w\d]+)", text) | |
| cent_m = re.search(r"Exam Centre is\s*(.*?)(?=Print Date|To,|The Centre|NAME OF EXAMINATION)", text, re.DOTALL) | |
| if name_m: info["name"] = name_m.group(1).split('\n')[0].strip() | |
| if roll_m: info["roll"] = roll_m.group(1).strip() | |
| if cent_m: info["center"] = " ".join(cent_m.group(1).split()) | |
| doc.close() | |
| except Exception as e: print(f"Error: {e}") | |
| return info | |
| # --- Playwright Logic --- | |
| async def download_jnvu_pdf(form_number): | |
| pdf_path = f"admit_card_{form_number}.pdf" | |
| async with async_playwright() as p: | |
| # Docker के लिए No-Sandbox बहुत जरूरी है | |
| browser = await p.chromium.launch(headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"]) | |
| context = await browser.new_context(accept_downloads=True) | |
| page = await context.new_page() | |
| try: | |
| url = "https://erp.jnvuiums.in/(S(biolzjtwlrcfmzwwzgs5uj5n))/Exam/Pre_Exam/Exam_ForALL_AdmitCard.aspx#" | |
| await page.goto(url, wait_until="load", timeout=40000) | |
| await page.fill("#txtchallanNo", str(form_number)) | |
| async with page.expect_download(timeout=20000) as download_info: | |
| await page.click("#btnGetResult") | |
| download = await download_info.value | |
| await download.save_as(pdf_path) | |
| return pdf_path | |
| except Exception as e: | |
| print(f"Download Error: {e}") | |
| return None | |
| finally: | |
| await browser.close() | |
| # --- Routes --- | |
| def index(): | |
| if request.method == 'POST': | |
| form_number = request.form.get('form_number') | |
| if not form_number.isdigit(): | |
| return render_template_string(HTML_PAGE, error="कृपया सही फॉर्म नंबर डालें।") | |
| file_path = asyncio.run(download_jnvu_pdf(form_number)) | |
| if file_path and os.path.exists(file_path): | |
| data = extract_student_info(file_path) | |
| return render_template_string(HTML_PAGE, data=data, filename=file_path) | |
| else: | |
| return render_template_string(HTML_PAGE, error="एडमिट कार्ड नहीं मिला या JNVU सर्वर धीमा है।") | |
| return render_template_string(HTML_PAGE) | |
| def download_file(filename): | |
| # सुरक्षा के लिए चेक करें कि फाइल मौजूद है | |
| if os.path.exists(filename): | |
| return send_file(filename, as_attachment=True) | |
| return "File missing", 404 | |
| if __name__ == "__main__": | |
| # Hugging Face PORT 7860 मांगता है | |
| port = int(os.environ.get("PORT", 7860)) | |
| app.run(host='0.0.0.0', port=port) | |