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 whoami, 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, first_name: str, last_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) name = f"{first_name} {last_name}" # 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 get_user_info(oauth_token): """Get user info from HF token.""" if oauth_token is None: return None, None, None, None try: user_info = whoami(oauth_token.token) username = user_info["name"] name_parts = user_info["fullname"].split(" ", 1) first_name = name_parts[0] last_name = name_parts[1] if len(name_parts) > 1 else "" profile_url = user_info["avatarUrl"] return username, first_name, last_name, profile_url except: return None, None, None, None 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", ) # 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): """Check certification status for logged-in user.""" if token is None: gr.Warning("Please log in to Hugging Face before checking certification!") return None, None, None, gr.Row.update(visible=False) username, first_name, last_name, profile_url = get_user_info(token) if not username: return ( "Please login with your Hugging Face account to check certification status", None, 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, first_name=first_name, last_name=last_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=pdf_path, label="Download Certificate"), gr.update(visible=result.passed, value=certificate_img, label="Certificate"), ) def create_gradio_interface(): """Create Gradio web interface with OAuth login.""" with gr.Blocks() as demo: gr.Markdown(""" # Get your Hugging Face Course Certificate 🎓 The certification process is completely free. To receive your certificate, you need to **pass 80% of the quiz**. There's **no deadlines, the course is self-paced**. Don't hesitate to share your certificate on Twitter (tag @huggingface) and on Linkedin. """) # Add login button gr.LoginButton() check_progress_button = gr.Button(value="Check My Progress") output_text = gr.Markdown(visible=False, sanitize_html=False) output_img = gr.Image(type="pil", visible=False) output_pdf = gr.File(visible=False) check_progress_button.click( fn=check_certification, outputs=[output_text, output_pdf, output_img], ).then( fn=join_finishers_org, ) return demo if __name__ == "__main__": demo = create_gradio_interface() demo.launch(debug=True)