File size: 8,149 Bytes
065a30a
75075b9
 
 
8adf41a
91cb2a2
75075b9
 
8adf41a
93e0d72
75075b9
 
93e0d72
8adf41a
dabc1e3
93e0d72
9350e9c
75075b9
 
 
 
421b84e
 
75075b9
 
 
 
 
421b84e
 
 
 
75075b9
 
 
 
 
 
 
421b84e
 
75075b9
 
 
 
421b84e
75075b9
421b84e
452a3a4
75075b9
 
 
421b84e
7a0686a
e9b5b0a
e183e91
75075b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8adf41a
75075b9
 
 
 
8adf41a
 
75075b9
 
 
dabc1e3
75075b9
8adf41a
 
75075b9
8adf41a
75075b9
 
 
 
 
 
 
 
 
 
9350e9c
75075b9
 
 
 
 
9350e9c
 
 
 
 
 
 
 
 
 
 
 
75075b9
421b84e
8adf41a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75075b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9350e9c
75075b9
 
 
 
 
 
 
 
 
 
 
8adf41a
 
 
75075b9
8adf41a
75075b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e9dc5d4
75075b9
 
e9dc5d4
75075b9
e9dc5d4
75075b9
 
 
 
 
e9dc5d4
75075b9
 
 
 
 
 
7a0686a
75075b9
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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"""
        <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):
    """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=certificate_img, label="Certificate"),
    )


def create_gradio_interface():
    """Create Gradio web interface with OAuth login."""
    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.
        """)

        # Add login button
        gr.LoginButton()

        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,
            outputs=[output_text, output_img],
        ).then(
            fn=join_finishers_org,
        )

    return demo


if __name__ == "__main__":
    demo = create_gradio_interface()
    demo.launch(debug=True)