Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| from io import BytesIO | |
| from datetime import date | |
| import tempfile | |
| import gradio as gr | |
| from PIL import Image, ImageDraw, ImageFont | |
| from huggingface_hub import upload_file | |
| from criteria import check_certification as check_certification_criteria | |
| from org import join_finishers_org | |
| CERTIFYING_ORG_LINKEDIN_ID = os.getenv("CERTIFYING_ORG_LINKEDIN_ID", "000000") | |
| COURSE_TITLE = os.getenv("COURSE_TITLE", "AI Agents Fundamentals") | |
| def download_profile_picture(profile_url: str): | |
| """Download profile picture from URL.""" | |
| response = requests.get(profile_url) | |
| return Image.open(BytesIO(response.content)) | |
| def generate_certificate(certificate_path: str, name: str, profile_url: str): | |
| """Generate certificate image and PDF.""" | |
| im = Image.open(certificate_path) | |
| d = ImageDraw.Draw(im) | |
| name_font = ImageFont.truetype("Quattrocento-Regular.ttf", 100) | |
| date_font = ImageFont.truetype("Quattrocento-Regular.ttf", 48) | |
| # Capitalize first letter of each name | |
| name = name.title() | |
| # Add name | |
| d.text((1000, 740), name, fill="black", anchor="mm", font=name_font) | |
| # Add profile picture just below the name | |
| profile_img = download_profile_picture(profile_url) | |
| profile_img = profile_img.resize((100, 100)) | |
| im.paste(im=profile_img, box=(350, 700)) | |
| # Add date | |
| d.text((1480, 1170), str(date.today()), fill="black", anchor="mm", font=date_font) | |
| # Save PDF | |
| pdf = im.convert("RGB") | |
| pdf.save("certificate.pdf") | |
| return im, "certificate.pdf" | |
| def create_linkedin_button(username: str, cert_url: str | None) -> str: | |
| """Create LinkedIn 'Add to Profile' button HTML.""" | |
| current_year = date.today().year | |
| current_month = date.today().month | |
| # Use the dataset certificate URL if available, otherwise fallback to default | |
| certificate_url = cert_url or "https://huggingface.co/agents-course-finishers" | |
| linkedin_params = { | |
| "startTask": "CERTIFICATION_NAME", | |
| "name": COURSE_TITLE, | |
| "organizationName": "Hugging Face", | |
| "organizationId": CERTIFYING_ORG_LINKEDIN_ID, | |
| "organizationIdissueYear": str(current_year), | |
| "issueMonth": str(current_month), | |
| "certUrl": certificate_url, | |
| "certId": username, # Using username as cert ID | |
| } | |
| # Build the LinkedIn button URL | |
| base_url = "https://www.linkedin.com/profile/add?" | |
| params = "&".join( | |
| f"{k}={requests.utils.quote(v)}" for k, v in linkedin_params.items() | |
| ) | |
| button_url = base_url + params | |
| message = f""" | |
| <a href="{button_url}" target="_blank" style="display: block; margin-top: 20px; text-align: center;"> | |
| <img src="https://download.linkedin.com/desktop/add2profile/buttons/en_US.png" | |
| alt="LinkedIn Add to Profile button"> | |
| </a> | |
| """ | |
| message += """ | |
| <a href="https://huggingface.co/agents-course-finishers" target="_blank" | |
| style="display: inline-block; background-color: #fff7e0; border: 2px solid #ffa500; | |
| border-radius: 10px; padding: 10px 20px; margin: 20px auto; text-align: center; | |
| text-decoration: none; color: #000; white-space: nowrap;"> | |
| <img src="https://agents-course-unit1-certification-app.hf.space/gradio_api/file=/usr/local/lib/python3.10/site-packages/gradio/icons/huggingface-logo.svg" | |
| style="display: inline-block; height: 20px; vertical-align: middle; margin-right: 10px;"> | |
| <span style="display: inline-block; vertical-align: middle; color: #000;">You are now an Agents Course Finisher</span> | |
| </a> | |
| """ | |
| return message | |
| def upload_certificate_to_hub(username: str, certificate_img) -> str: | |
| """Upload certificate to the dataset hub and return the URL.""" | |
| # Save image to temporary file | |
| with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp: | |
| certificate_img.save(tmp.name) | |
| try: | |
| # Upload file to hub | |
| repo_id = "agents-course/certificates" | |
| path_in_repo = f"certificates/{username}/{date.today()}.png" | |
| upload_file( | |
| path_or_fileobj=tmp.name, | |
| path_in_repo=path_in_repo, | |
| repo_id=repo_id, | |
| repo_type="dataset", | |
| ) | |
| # Construct the URL to the image | |
| cert_url = ( | |
| f"https://huggingface.co/datasets/{repo_id}/resolve/main/{path_in_repo}" | |
| ) | |
| # Clean up temp file | |
| os.unlink(tmp.name) | |
| return cert_url | |
| except Exception as e: | |
| print(f"Error uploading certificate: {e}") | |
| os.unlink(tmp.name) | |
| return None | |
| def check_certification( | |
| token: gr.OAuthToken | None, | |
| profile: gr.OAuthProfile | None, | |
| custom_name: str | None = None, | |
| ): | |
| """Check certification status for logged-in user.""" | |
| if token is None or profile is None: | |
| gr.Warning("Please log in to Hugging Face before checking certification!") | |
| return None, None, gr.Row.update(visible=False) | |
| username = profile.username | |
| # Use custom name if provided, otherwise fall back to profile name | |
| name = custom_name.strip() if custom_name and custom_name.strip() else profile.name | |
| profile_url = profile.picture | |
| if not username: | |
| return ( | |
| "Please login with your Hugging Face account to check certification status", | |
| None, | |
| gr.Row.update(visible=False), | |
| ) | |
| # Check certification criteria | |
| gr.Info("Collecting data from your course progress...") | |
| result = check_certification_criteria(username) | |
| # Generate certificate if passed | |
| if result.passed and result.certificate_path: | |
| certificate_img, pdf_path = generate_certificate( | |
| certificate_path=result.certificate_path, | |
| name=name, | |
| profile_url=profile_url, | |
| ) | |
| # Upload certificate to hub and get URL | |
| cert_url = upload_certificate_to_hub(username, certificate_img) | |
| # Add LinkedIn button for passed certificates | |
| linkedin_button = create_linkedin_button(username, cert_url) | |
| result_message = f"{result.message}\n\n{linkedin_button}" | |
| else: | |
| certificate_img = None | |
| pdf_path = None | |
| result_message = result.message | |
| return ( | |
| gr.update(visible=True, value=result_message, label="Grade"), | |
| gr.update(visible=result.passed, value=certificate_img, label="Certificate"), | |
| ) | |
| with gr.Blocks() as demo: | |
| gr.Markdown( | |
| """ | |
| # Get your Certificate of Fundamentals of Agents 🎓 | |
| The certification process is completely free. | |
| To earn this certificate, you need to complete <a href="https://hf.co/learn/agents-course/unit1/introduction" alt="Agent Course Unit 1"/>Unit 1 of the Agents Course</a> and **pass 80% of the final quiz**. | |
| Once you receive your certificate, don't hesitate to share it on X (and tag @huggingface) as well as on LinkedIn so that we can congratulate you. | |
| """ | |
| ) | |
| with gr.Row(): | |
| # Add login button | |
| gr.LoginButton() | |
| # Add optional name input | |
| custom_name_input = gr.Textbox( | |
| label="Custom Name (Optional)", | |
| placeholder="Enter your name as you want it to appear on the certificate", | |
| info="Leave empty to use your Hugging Face profile name", | |
| ) | |
| check_progress_button = gr.Button(value="Get My Certificate") | |
| output_text = gr.Markdown(visible=False, sanitize_html=False) | |
| output_img = gr.Image(type="pil", visible=False) | |
| check_progress_button.click( | |
| fn=check_certification, | |
| inputs=[custom_name_input], | |
| outputs=[output_text, output_img], | |
| ).then( | |
| fn=join_finishers_org, | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(debug=True) | |