burtenshaw commited on
Commit
55df867
·
1 Parent(s): 737e7e9

add filler page

Browse files
Files changed (2) hide show
  1. _app.py +229 -0
  2. app.py +8 -224
_app.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from io import BytesIO
4
+ from datetime import date
5
+ import tempfile
6
+
7
+ import gradio as gr
8
+ from PIL import Image, ImageDraw, ImageFont
9
+ from huggingface_hub import upload_file
10
+
11
+ from criteria import check_certification as check_certification_criteria
12
+ from org import join_finishers_org
13
+
14
+ CERTIFYING_ORG_LINKEDIN_ID = os.getenv("CERTIFYING_ORG_LINKEDIN_ID", "000000")
15
+ COURSE_TITLE = os.getenv("COURSE_TITLE", "AI Agents Fundamentals")
16
+
17
+
18
+ def download_profile_picture(profile_url: str):
19
+ """Download profile picture from URL."""
20
+ try:
21
+ response = requests.get(profile_url)
22
+ response.raise_for_status() # Ensure we got a proper response
23
+ return Image.open(BytesIO(response.content))
24
+ except Exception as e:
25
+ print(f"Error downloading profile picture from {profile_url}: {e}")
26
+ # Return a default fallback image (a plain white 100x100 image)
27
+ return Image.new("RGB", (100, 100), color="white")
28
+
29
+
30
+ def generate_certificate(certificate_path: str, name: str, profile_url: str):
31
+ """Generate certificate image and PDF."""
32
+ im = Image.open(certificate_path)
33
+ d = ImageDraw.Draw(im)
34
+
35
+ name_font = ImageFont.truetype("Quattrocento-Regular.ttf", 100)
36
+ date_font = ImageFont.truetype("Quattrocento-Regular.ttf", 48)
37
+
38
+ # Capitalize first letter of each name
39
+ name = name.title()
40
+
41
+ # Add name
42
+ d.text((1000, 740), name, fill="black", anchor="mm", font=name_font)
43
+
44
+ # Add profile picture just below the name
45
+ profile_img = download_profile_picture(profile_url)
46
+ profile_img = profile_img.resize((100, 100))
47
+ im.paste(im=profile_img, box=(350, 700))
48
+
49
+ # Add date
50
+ d.text((1480, 1170), str(date.today()), fill="black", anchor="mm", font=date_font)
51
+
52
+ # Save PDF
53
+ pdf = im.convert("RGB")
54
+ pdf.save("certificate.pdf")
55
+
56
+ return im, "certificate.pdf"
57
+
58
+
59
+ def create_linkedin_button(username: str, cert_url: str | None) -> str:
60
+ """Create LinkedIn 'Add to Profile' button HTML."""
61
+ current_year = date.today().year
62
+ current_month = date.today().month
63
+
64
+ # Use the dataset certificate URL if available, otherwise fallback to default
65
+ certificate_url = cert_url or "https://huggingface.co/agents-course-finishers"
66
+
67
+ linkedin_params = {
68
+ "startTask": "CERTIFICATION_NAME",
69
+ "name": COURSE_TITLE,
70
+ "organizationName": "Hugging Face",
71
+ "organizationId": CERTIFYING_ORG_LINKEDIN_ID,
72
+ "organizationIdissueYear": str(current_year),
73
+ "issueMonth": str(current_month),
74
+ "certUrl": certificate_url,
75
+ "certId": username, # Using username as cert ID
76
+ }
77
+
78
+ # Build the LinkedIn button URL
79
+ base_url = "https://www.linkedin.com/profile/add?"
80
+ params = "&".join(
81
+ f"{k}={requests.utils.quote(v)}" for k, v in linkedin_params.items()
82
+ )
83
+ button_url = base_url + params
84
+
85
+ message = f"""
86
+ <a href="{button_url}" target="_blank" style="display: block; margin-top: 20px; text-align: center;">
87
+ <img src="https://download.linkedin.com/desktop/add2profile/buttons/en_US.png"
88
+ alt="LinkedIn Add to Profile button">
89
+ </a>
90
+ """
91
+ message += """
92
+ <a href="https://huggingface.co/agents-course-finishers" target="_blank"
93
+ style="display: inline-block; background-color: #fff7e0; border: 2px solid #ffa500;
94
+ border-radius: 10px; padding: 10px 20px; margin: 20px auto; text-align: center;
95
+ text-decoration: none; color: #000; white-space: nowrap;">
96
+ <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"
97
+ style="display: inline-block; height: 20px; vertical-align: middle; margin-right: 10px;">
98
+ <span style="display: inline-block; vertical-align: middle; color: #000;">You are now an Agents Course Finisher</span>
99
+ </a>
100
+ """
101
+
102
+ return message
103
+
104
+
105
+ def upload_certificate_to_hub(username: str, certificate_img) -> str:
106
+ """Upload certificate to the dataset hub and return the URL."""
107
+ # Save image to temporary file
108
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
109
+ certificate_img.save(tmp.name)
110
+
111
+ try:
112
+ # Upload file to hub
113
+ repo_id = "agents-course/certificates"
114
+ path_in_repo = f"certificates/{username}/{date.today()}.png"
115
+
116
+ upload_file(
117
+ path_or_fileobj=tmp.name,
118
+ path_in_repo=path_in_repo,
119
+ repo_id=repo_id,
120
+ repo_type="dataset",
121
+ token=os.getenv("HF_TOKEN"),
122
+ )
123
+
124
+ # Construct the URL to the image
125
+ cert_url = (
126
+ f"https://huggingface.co/datasets/{repo_id}/resolve/main/{path_in_repo}"
127
+ )
128
+
129
+ # Clean up temp file
130
+ os.unlink(tmp.name)
131
+
132
+ return cert_url
133
+ except Exception as e:
134
+ print(f"Error uploading certificate: {e}")
135
+ os.unlink(tmp.name)
136
+ return None
137
+
138
+
139
+ def check_certification(
140
+ token: gr.OAuthToken | None,
141
+ profile: gr.OAuthProfile | None,
142
+ custom_name: str | None = None,
143
+ ):
144
+ """Check certification status for logged-in user."""
145
+ if token is None or profile is None:
146
+ gr.Warning("Please log in to Hugging Face before checking certification!")
147
+ return None, None, gr.Row.update(visible=False)
148
+
149
+ username = profile.username
150
+ username = "meke-ops"
151
+ # Use custom name if provided, otherwise fall back to profile name
152
+ name = custom_name.strip() if custom_name and custom_name.strip() else profile.name
153
+ profile_url = profile.picture
154
+
155
+ if not username:
156
+ return (
157
+ "Please login with your Hugging Face account to check certification status",
158
+ None,
159
+ gr.Row.update(visible=False),
160
+ )
161
+
162
+ # Check certification criteria
163
+ gr.Info("Collecting data from your course progress...")
164
+ result = check_certification_criteria(username)
165
+
166
+ # Generate certificate if passed
167
+ if result.passed and result.certificate_path:
168
+ certificate_img, pdf_path = generate_certificate(
169
+ certificate_path=result.certificate_path,
170
+ name=name,
171
+ profile_url=profile_url,
172
+ )
173
+
174
+ # Upload certificate to hub and get URL
175
+ cert_url = upload_certificate_to_hub(username, certificate_img)
176
+
177
+ # Add LinkedIn button for passed certificates
178
+ linkedin_button = create_linkedin_button(username, cert_url)
179
+ result_message = f"{result.message}\n\n{linkedin_button}"
180
+ else:
181
+ certificate_img = None
182
+ pdf_path = None
183
+ result_message = result.message
184
+
185
+ return (
186
+ gr.update(visible=True, value=result_message, label="Grade"),
187
+ gr.update(visible=result.passed, value=certificate_img, label="Certificate"),
188
+ )
189
+
190
+
191
+ with gr.Blocks() as demo:
192
+ gr.Markdown(
193
+ """
194
+ # Get your Certificate of Fundamentals of Agents 🎓
195
+ The certification process is completely free.
196
+
197
+ 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**.
198
+
199
+ 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.
200
+ """
201
+ )
202
+
203
+ with gr.Row():
204
+ # Add login button
205
+ gr.LoginButton()
206
+
207
+ # Add optional name input
208
+ custom_name_input = gr.Textbox(
209
+ label="Custom Name (Optional)",
210
+ placeholder="Enter your name as you want it to appear on the certificate",
211
+ info="Leave empty to use your Hugging Face profile name",
212
+ )
213
+
214
+ check_progress_button = gr.Button(value="Get My Certificate")
215
+
216
+ output_text = gr.Markdown(visible=False, sanitize_html=False)
217
+ output_img = gr.Image(type="pil", visible=False)
218
+
219
+ check_progress_button.click(
220
+ fn=check_certification,
221
+ inputs=[custom_name_input],
222
+ outputs=[output_text, output_img],
223
+ ).then(
224
+ fn=join_finishers_org,
225
+ )
226
+
227
+
228
+ if __name__ == "__main__":
229
+ demo.launch(debug=True)
app.py CHANGED
@@ -1,229 +1,13 @@
1
- import os
2
- import requests
3
- from io import BytesIO
4
- from datetime import date
5
- import tempfile
6
-
7
  import gradio as gr
8
- from PIL import Image, ImageDraw, ImageFont
9
- from huggingface_hub import upload_file
10
-
11
- from criteria import check_certification as check_certification_criteria
12
- from org import join_finishers_org
13
-
14
- CERTIFYING_ORG_LINKEDIN_ID = os.getenv("CERTIFYING_ORG_LINKEDIN_ID", "000000")
15
- COURSE_TITLE = os.getenv("COURSE_TITLE", "AI Agents Fundamentals")
16
-
17
-
18
- def download_profile_picture(profile_url: str):
19
- """Download profile picture from URL."""
20
- try:
21
- response = requests.get(profile_url)
22
- response.raise_for_status() # Ensure we got a proper response
23
- return Image.open(BytesIO(response.content))
24
- except Exception as e:
25
- print(f"Error downloading profile picture from {profile_url}: {e}")
26
- # Return a default fallback image (a plain white 100x100 image)
27
- return Image.new("RGB", (100, 100), color="white")
28
-
29
-
30
- def generate_certificate(certificate_path: str, name: str, profile_url: str):
31
- """Generate certificate image and PDF."""
32
- im = Image.open(certificate_path)
33
- d = ImageDraw.Draw(im)
34
-
35
- name_font = ImageFont.truetype("Quattrocento-Regular.ttf", 100)
36
- date_font = ImageFont.truetype("Quattrocento-Regular.ttf", 48)
37
-
38
- # Capitalize first letter of each name
39
- name = name.title()
40
-
41
- # Add name
42
- d.text((1000, 740), name, fill="black", anchor="mm", font=name_font)
43
-
44
- # Add profile picture just below the name
45
- profile_img = download_profile_picture(profile_url)
46
- profile_img = profile_img.resize((100, 100))
47
- im.paste(im=profile_img, box=(350, 700))
48
-
49
- # Add date
50
- d.text((1480, 1170), str(date.today()), fill="black", anchor="mm", font=date_font)
51
-
52
- # Save PDF
53
- pdf = im.convert("RGB")
54
- pdf.save("certificate.pdf")
55
-
56
- return im, "certificate.pdf"
57
-
58
-
59
- def create_linkedin_button(username: str, cert_url: str | None) -> str:
60
- """Create LinkedIn 'Add to Profile' button HTML."""
61
- current_year = date.today().year
62
- current_month = date.today().month
63
-
64
- # Use the dataset certificate URL if available, otherwise fallback to default
65
- certificate_url = cert_url or "https://huggingface.co/agents-course-finishers"
66
-
67
- linkedin_params = {
68
- "startTask": "CERTIFICATION_NAME",
69
- "name": COURSE_TITLE,
70
- "organizationName": "Hugging Face",
71
- "organizationId": CERTIFYING_ORG_LINKEDIN_ID,
72
- "organizationIdissueYear": str(current_year),
73
- "issueMonth": str(current_month),
74
- "certUrl": certificate_url,
75
- "certId": username, # Using username as cert ID
76
- }
77
-
78
- # Build the LinkedIn button URL
79
- base_url = "https://www.linkedin.com/profile/add?"
80
- params = "&".join(
81
- f"{k}={requests.utils.quote(v)}" for k, v in linkedin_params.items()
82
- )
83
- button_url = base_url + params
84
-
85
- message = f"""
86
- <a href="{button_url}" target="_blank" style="display: block; margin-top: 20px; text-align: center;">
87
- <img src="https://download.linkedin.com/desktop/add2profile/buttons/en_US.png"
88
- alt="LinkedIn Add to Profile button">
89
- </a>
90
- """
91
- message += """
92
- <a href="https://huggingface.co/agents-course-finishers" target="_blank"
93
- style="display: inline-block; background-color: #fff7e0; border: 2px solid #ffa500;
94
- border-radius: 10px; padding: 10px 20px; margin: 20px auto; text-align: center;
95
- text-decoration: none; color: #000; white-space: nowrap;">
96
- <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"
97
- style="display: inline-block; height: 20px; vertical-align: middle; margin-right: 10px;">
98
- <span style="display: inline-block; vertical-align: middle; color: #000;">You are now an Agents Course Finisher</span>
99
- </a>
100
- """
101
-
102
- return message
103
-
104
-
105
- def upload_certificate_to_hub(username: str, certificate_img) -> str:
106
- """Upload certificate to the dataset hub and return the URL."""
107
- # Save image to temporary file
108
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
109
- certificate_img.save(tmp.name)
110
-
111
- try:
112
- # Upload file to hub
113
- repo_id = "agents-course/certificates"
114
- path_in_repo = f"certificates/{username}/{date.today()}.png"
115
-
116
- upload_file(
117
- path_or_fileobj=tmp.name,
118
- path_in_repo=path_in_repo,
119
- repo_id=repo_id,
120
- repo_type="dataset",
121
- token=os.getenv("HF_TOKEN"),
122
- )
123
-
124
- # Construct the URL to the image
125
- cert_url = (
126
- f"https://huggingface.co/datasets/{repo_id}/resolve/main/{path_in_repo}"
127
- )
128
-
129
- # Clean up temp file
130
- os.unlink(tmp.name)
131
-
132
- return cert_url
133
- except Exception as e:
134
- print(f"Error uploading certificate: {e}")
135
- os.unlink(tmp.name)
136
- return None
137
-
138
-
139
- def check_certification(
140
- token: gr.OAuthToken | None,
141
- profile: gr.OAuthProfile | None,
142
- custom_name: str | None = None,
143
- ):
144
- """Check certification status for logged-in user."""
145
- if token is None or profile is None:
146
- gr.Warning("Please log in to Hugging Face before checking certification!")
147
- return None, None, gr.Row.update(visible=False)
148
-
149
- username = profile.username
150
-
151
- # Use custom name if provided, otherwise fall back to profile name
152
- name = custom_name.strip() if custom_name and custom_name.strip() else profile.name
153
- profile_url = profile.picture
154
-
155
- if not username:
156
- return (
157
- "Please login with your Hugging Face account to check certification status",
158
- None,
159
- gr.Row.update(visible=False),
160
- )
161
-
162
- # Check certification criteria
163
- gr.Info("Collecting data from your course progress...")
164
- result = check_certification_criteria(username)
165
-
166
- # Generate certificate if passed
167
- if result.passed and result.certificate_path:
168
- certificate_img, pdf_path = generate_certificate(
169
- certificate_path=result.certificate_path,
170
- name=name,
171
- profile_url=profile_url,
172
- )
173
-
174
- # Upload certificate to hub and get URL
175
- cert_url = upload_certificate_to_hub(username, certificate_img)
176
-
177
- # Add LinkedIn button for passed certificates
178
- linkedin_button = create_linkedin_button(username, cert_url)
179
- result_message = f"{result.message}\n\n{linkedin_button}"
180
- else:
181
- certificate_img = None
182
- pdf_path = None
183
- result_message = result.message
184
-
185
- return (
186
- gr.update(visible=True, value=result_message, label="Grade"),
187
- gr.update(visible=result.passed, value=certificate_img, label="Certificate"),
188
- )
189
-
190
 
191
  with gr.Blocks() as demo:
192
- gr.Markdown(
193
- """
194
- # Get your Certificate of Fundamentals of Agents 🎓
195
- The certification process is completely free.
196
 
197
- 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**.
198
-
199
- 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.
 
 
200
  """
201
- )
202
-
203
- with gr.Row():
204
- # Add login button
205
- gr.LoginButton()
206
-
207
- # Add optional name input
208
- custom_name_input = gr.Textbox(
209
- label="Custom Name (Optional)",
210
- placeholder="Enter your name as you want it to appear on the certificate",
211
- info="Leave empty to use your Hugging Face profile name",
212
- )
213
-
214
- check_progress_button = gr.Button(value="Get My Certificate")
215
-
216
- output_text = gr.Markdown(visible=False, sanitize_html=False)
217
- output_img = gr.Image(type="pil", visible=False)
218
-
219
- check_progress_button.click(
220
- fn=check_certification,
221
- inputs=[custom_name_input],
222
- outputs=[output_text, output_img],
223
- ).then(
224
- fn=join_finishers_org,
225
- )
226
-
227
-
228
- if __name__ == "__main__":
229
- demo.launch(debug=True)
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  with gr.Blocks() as demo:
 
 
 
 
4
 
5
+ text = """# 🕊️ The Aents course certification has moved!
6
+
7
+ Please visit the quiz app which will now generate certificates:
8
+ https://huggingface.co/spaces/agents-course/unit_1_quiz
9
+
10
  """
11
+ gr.Markdown(text)
12
+
13
+ demo.launch()