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.""" try: response = requests.get(profile_url) response.raise_for_status() # Ensure we got a proper response return Image.open(BytesIO(response.content)) except Exception as e: print(f"Error downloading profile picture from {profile_url}: {e}") # Return a default fallback image (a plain white 100x100 image) return Image.new("RGB", (100, 100), color="white") 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""" LinkedIn Add to Profile button """ message += """ You are now an Agents Course Finisher """ 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", token=os.getenv("HF_TOKEN"), ) # 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 Unit 1 of the Agents Course 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)