phonglanvq002 commited on
Commit
69e072b
·
verified ·
1 Parent(s): 36a3e72

Upload folder using huggingface_hub

Browse files
k12/img_generator.py CHANGED
@@ -48,7 +48,7 @@ CERTIFICATIONS = [
48
  ]
49
 
50
 
51
- def _generate_teacher_data(first_name: str, last_name: str, school_name: str = "Springfield High School", email_domain: str = None) -> dict:
52
  """Generate consistent teacher data for all templates."""
53
  full_name = f"{first_name} {last_name}"
54
  initials = f"{first_name[0]}{last_name[0]}"
@@ -110,6 +110,20 @@ def _generate_teacher_data(first_name: str, last_name: str, school_name: str = "
110
  "CERT_DATE": cert_date.strftime("%m/%Y"),
111
  "SCHOOL_NAME": school_name,
112
  "DISTRICT_NAME": district_name,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  }
114
 
115
 
@@ -163,7 +177,7 @@ def generate_teacher_png(first_name: str, last_name: str) -> bytes:
163
  return png_bytes
164
 
165
 
166
- def generate_both_pngs(first_name: str, last_name: str, school_name: str = "Springfield High School", email_domain: str = None) -> tuple:
167
  """Generate both Payroll and Staff Portal screenshots as PNGs.
168
 
169
  Args:
@@ -171,6 +185,7 @@ def generate_both_pngs(first_name: str, last_name: str, school_name: str = "Spri
171
  last_name: Teacher's last name
172
  school_name: School name from SheerID
173
  email_domain: Custom email domain (e.g., cityname.k12.state.us)
 
174
 
175
  Returns:
176
  tuple: (payroll_png_bytes, staff_png_bytes)
@@ -183,7 +198,7 @@ def generate_both_pngs(first_name: str, last_name: str, school_name: str = "Spri
183
  ) from exc
184
 
185
  # Use same data for consistency across both documents
186
- data = _generate_teacher_data(first_name, last_name, school_name, email_domain)
187
 
188
  payroll_html = _render_template("template_payroll.html", data)
189
  staff_html = _render_template("template_staff.html", data)
 
48
  ]
49
 
50
 
51
+ def _generate_teacher_data(first_name: str, last_name: str, school_name: str = "Springfield High School", email_domain: str = None, logo_url: str = None) -> dict:
52
  """Generate consistent teacher data for all templates."""
53
  full_name = f"{first_name} {last_name}"
54
  initials = f"{first_name[0]}{last_name[0]}"
 
110
  "CERT_DATE": cert_date.strftime("%m/%Y"),
111
  "SCHOOL_NAME": school_name,
112
  "DISTRICT_NAME": district_name,
113
+ # Dynamic payroll dates (recent)
114
+ "PAY_DATE_1": (datetime.now() - timedelta(days=5)).strftime("%m/%d/%Y"),
115
+ "PAY_DATE_2": (datetime.now() - timedelta(days=20)).strftime("%m/%d/%Y"),
116
+ "PAY_DATE_3": (datetime.now() - timedelta(days=35)).strftime("%m/%d/%Y"),
117
+ "PAY_PERIOD_1_START": (datetime.now() - timedelta(days=20)).strftime("%m/%d/%Y"),
118
+ "PAY_PERIOD_1_END": (datetime.now() - timedelta(days=6)).strftime("%m/%d/%Y"),
119
+ "PAY_PERIOD_2_START": (datetime.now() - timedelta(days=35)).strftime("%m/%d/%Y"),
120
+ "PAY_PERIOD_2_END": (datetime.now() - timedelta(days=21)).strftime("%m/%d/%Y"),
121
+ "PAY_PERIOD_3_START": (datetime.now() - timedelta(days=50)).strftime("%m/%d/%Y"),
122
+ "PAY_PERIOD_3_END": (datetime.now() - timedelta(days=36)).strftime("%m/%d/%Y"),
123
+ "SCHOOL_YEAR": f"{datetime.now().year-1}-{datetime.now().year}" if datetime.now().month < 7 else f"{datetime.now().year}-{datetime.now().year+1}",
124
+ # Logo - use provided URL or default initials
125
+ "LOGO_HTML": f'<img src="{logo_url}" alt="School Logo" style="max-width: 45px; max-height: 45px; object-fit: contain;">' if logo_url else '{{DISTRICT_INITIALS}}',
126
+ "DISTRICT_INITIALS": school_name[:3].upper() if len(school_name) >= 3 else school_name.upper(),
127
  }
128
 
129
 
 
177
  return png_bytes
178
 
179
 
180
+ def generate_both_pngs(first_name: str, last_name: str, school_name: str = "Springfield High School", email_domain: str = None, logo_url: str = None) -> tuple:
181
  """Generate both Payroll and Staff Portal screenshots as PNGs.
182
 
183
  Args:
 
185
  last_name: Teacher's last name
186
  school_name: School name from SheerID
187
  email_domain: Custom email domain (e.g., cityname.k12.state.us)
188
+ logo_url: URL to school logo image
189
 
190
  Returns:
191
  tuple: (payroll_png_bytes, staff_png_bytes)
 
198
  ) from exc
199
 
200
  # Use same data for consistency across both documents
201
+ data = _generate_teacher_data(first_name, last_name, school_name, email_domain, logo_url)
202
 
203
  payroll_html = _render_template("template_payroll.html", data)
204
  staff_html = _render_template("template_staff.html", data)
k12/sheerid_verifier.py CHANGED
@@ -113,6 +113,7 @@ class SheerIDVerifier:
113
  def verify(self, first_name: str = None, last_name: str = None,
114
  email: str = None, birth_date: str = None,
115
  school_id: str = None, email_domain: str = None,
 
116
  hcaptcha_token: str = None, turnstile_token: str = None) -> Dict:
117
  """
118
  Execute full verification flow, removing status polling to reduce time consumption
@@ -139,12 +140,13 @@ class SheerIDVerifier:
139
  logger.info(f"Email: {email}")
140
  logger.info(f"School: {school['name']}")
141
  logger.info(f"Email Domain: {email_domain or 'auto-generated'}")
 
142
  logger.info(f"Birthday: {birth_date}")
143
  logger.info(f"Verification ID: {self.verification_id}")
144
 
145
  # Generate both teacher verification screenshots
146
  logger.info("Step 1/4: Generating teacher verification documents...")
147
- payroll_png, staff_png = generate_both_pngs(first_name, last_name, school['name'], email_domain)
148
  payroll_size = len(payroll_png)
149
  staff_size = len(staff_png)
150
  logger.info(f"✓ Payroll Portal: {payroll_size / 1024:.2f}KB, Staff Portal: {staff_size / 1024:.2f}KB")
 
113
  def verify(self, first_name: str = None, last_name: str = None,
114
  email: str = None, birth_date: str = None,
115
  school_id: str = None, email_domain: str = None,
116
+ logo_url: str = None,
117
  hcaptcha_token: str = None, turnstile_token: str = None) -> Dict:
118
  """
119
  Execute full verification flow, removing status polling to reduce time consumption
 
140
  logger.info(f"Email: {email}")
141
  logger.info(f"School: {school['name']}")
142
  logger.info(f"Email Domain: {email_domain or 'auto-generated'}")
143
+ logger.info(f"Logo URL: {logo_url or 'default'}")
144
  logger.info(f"Birthday: {birth_date}")
145
  logger.info(f"Verification ID: {self.verification_id}")
146
 
147
  # Generate both teacher verification screenshots
148
  logger.info("Step 1/4: Generating teacher verification documents...")
149
+ payroll_png, staff_png = generate_both_pngs(first_name, last_name, school['name'], email_domain, logo_url)
150
  payroll_size = len(payroll_png)
151
  staff_size = len(staff_png)
152
  logger.info(f"✓ Payroll Portal: {payroll_size / 1024:.2f}KB, Staff Portal: {staff_size / 1024:.2f}KB")
k12/template_payroll.html CHANGED
@@ -226,7 +226,7 @@
226
  <div class="container">
227
  <div class="header">
228
  <div class="logo-area">
229
- <div class="logo">SSD</div>
230
  <div class="district-info">
231
  <h1>{{DISTRICT_NAME}}</h1>
232
  <span>Employee Self Service Portal - Skyward</span>
@@ -295,7 +295,7 @@
295
  </div>
296
 
297
  <div class="payroll-section">
298
- <h3>Recent Pay History (2024-2025 School Year)</h3>
299
  <table class="payroll-table">
300
  <thead>
301
  <tr>
@@ -308,22 +308,22 @@
308
  </thead>
309
  <tbody>
310
  <tr>
311
- <td>12/20/2024</td>
312
- <td>12/01/2024 - 12/15/2024</td>
313
  <td>$2,847.50</td>
314
  <td>$712.38</td>
315
  <td class="amount">$2,135.12</td>
316
  </tr>
317
  <tr>
318
- <td>12/05/2024</td>
319
- <td>11/16/2024 - 11/30/2024</td>
320
  <td>$2,847.50</td>
321
  <td>$712.38</td>
322
  <td class="amount">$2,135.12</td>
323
  </tr>
324
  <tr>
325
- <td>11/20/2024</td>
326
- <td>11/01/2024 - 11/15/2024</td>
327
  <td>$2,847.50</td>
328
  <td>$712.38</td>
329
  <td class="amount">$2,135.12</td>
 
226
  <div class="container">
227
  <div class="header">
228
  <div class="logo-area">
229
+ <div class="logo">{{LOGO_HTML}}</div>
230
  <div class="district-info">
231
  <h1>{{DISTRICT_NAME}}</h1>
232
  <span>Employee Self Service Portal - Skyward</span>
 
295
  </div>
296
 
297
  <div class="payroll-section">
298
+ <h3>Recent Pay History ({{SCHOOL_YEAR}} School Year)</h3>
299
  <table class="payroll-table">
300
  <thead>
301
  <tr>
 
308
  </thead>
309
  <tbody>
310
  <tr>
311
+ <td>{{PAY_DATE_1}}</td>
312
+ <td>{{PAY_PERIOD_1_START}} - {{PAY_PERIOD_1_END}}</td>
313
  <td>$2,847.50</td>
314
  <td>$712.38</td>
315
  <td class="amount">$2,135.12</td>
316
  </tr>
317
  <tr>
318
+ <td>{{PAY_DATE_2}}</td>
319
+ <td>{{PAY_PERIOD_2_START}} - {{PAY_PERIOD_2_END}}</td>
320
  <td>$2,847.50</td>
321
  <td>$712.38</td>
322
  <td class="amount">$2,135.12</td>
323
  </tr>
324
  <tr>
325
+ <td>{{PAY_DATE_3}}</td>
326
+ <td>{{PAY_PERIOD_3_START}} - {{PAY_PERIOD_3_END}}</td>
327
  <td>$2,847.50</td>
328
  <td>$712.38</td>
329
  <td class="amount">$2,135.12</td>
k12/template_staff.html CHANGED
@@ -261,7 +261,7 @@
261
  <div class="container">
262
  <div class="header">
263
  <div class="logo-section">
264
- <div class="school-logo">SSD</div>
265
  <div class="school-name">
266
  <h1>Springfield School District</h1>
267
  <p>Excellence in Education Since 1952</p>
 
261
  <div class="container">
262
  <div class="header">
263
  <div class="logo-section">
264
+ <div class="school-logo">{{LOGO_HTML}}</div>
265
  <div class="school-name">
266
  <h1>Springfield School District</h1>
267
  <p>Excellence in Education Since 1952</p>
web/api/preview.py CHANGED
@@ -22,6 +22,7 @@ class PreviewRequest(BaseModel):
22
  last_name: Optional[str] = "Doe"
23
  school_name: Optional[str] = "Springfield High School"
24
  email_domain: Optional[str] = None
 
25
 
26
 
27
  class PreviewResponse(BaseModel):
@@ -32,7 +33,7 @@ class PreviewResponse(BaseModel):
32
  error: Optional[str] = None
33
 
34
 
35
- def _generate_previews(first_name: str, last_name: str, school_name: str, email_domain: str = None):
36
  """Sync function to generate previews - runs in thread pool"""
37
  # Add parent dir to path
38
  if _PARENT_DIR not in sys.path:
@@ -50,7 +51,8 @@ def _generate_previews(first_name: str, last_name: str, school_name: str, email_
50
  first_name=first_name,
51
  last_name=last_name,
52
  school_name=school_name,
53
- email_domain=email_domain
 
54
  )
55
 
56
  return payroll_png, staff_png
@@ -71,7 +73,8 @@ async def preview_k12_documents(request: PreviewRequest) -> PreviewResponse:
71
  request.first_name or "John",
72
  request.last_name or "Doe",
73
  request.school_name or "Springfield High School",
74
- request.email_domain
 
75
  )
76
 
77
  # Convert to base64
 
22
  last_name: Optional[str] = "Doe"
23
  school_name: Optional[str] = "Springfield High School"
24
  email_domain: Optional[str] = None
25
+ logo_url: Optional[str] = None
26
 
27
 
28
  class PreviewResponse(BaseModel):
 
33
  error: Optional[str] = None
34
 
35
 
36
+ def _generate_previews(first_name: str, last_name: str, school_name: str, email_domain: str = None, logo_url: str = None):
37
  """Sync function to generate previews - runs in thread pool"""
38
  # Add parent dir to path
39
  if _PARENT_DIR not in sys.path:
 
51
  first_name=first_name,
52
  last_name=last_name,
53
  school_name=school_name,
54
+ email_domain=email_domain,
55
+ logo_url=logo_url
56
  )
57
 
58
  return payroll_png, staff_png
 
73
  request.first_name or "John",
74
  request.last_name or "Doe",
75
  request.school_name or "Springfield High School",
76
+ request.email_domain,
77
+ request.logo_url
78
  )
79
 
80
  # Convert to base64
web/api/verify.py CHANGED
@@ -65,6 +65,7 @@ class VerifyRequest(BaseModel):
65
  birth_date: Optional[str] = None
66
  school_id: Optional[str] = None
67
  email_domain: Optional[str] = None # For K12: custom email domain
 
68
 
69
 
70
  class VerifyResponse(BaseModel):
@@ -122,7 +123,8 @@ async def verify(verify_type: str, request: VerifyRequest) -> VerifyResponse:
122
  email=request.email,
123
  birth_date=request.birth_date,
124
  school_id=request.school_id,
125
- email_domain=request.email_domain
 
126
  )
127
 
128
  return VerifyResponse(
 
65
  birth_date: Optional[str] = None
66
  school_id: Optional[str] = None
67
  email_domain: Optional[str] = None # For K12: custom email domain
68
+ logo_url: Optional[str] = None # For K12: school logo URL
69
 
70
 
71
  class VerifyResponse(BaseModel):
 
123
  email=request.email,
124
  birth_date=request.birth_date,
125
  school_id=request.school_id,
126
+ email_domain=request.email_domain,
127
+ logo_url=request.logo_url
128
  )
129
 
130
  return VerifyResponse(
web/static/index.html CHANGED
@@ -93,6 +93,13 @@
93
  <small style="color: var(--text-secondary); font-size: 0.8rem;">
94
  Format: cityname.k12.state.us (e.g., boston.k12.ma.us)
95
  </small>
 
 
 
 
 
 
 
96
  </div>
97
 
98
  <div class="form-group collapsible">
 
93
  <small style="color: var(--text-secondary); font-size: 0.8rem;">
94
  Format: cityname.k12.state.us (e.g., boston.k12.ma.us)
95
  </small>
96
+
97
+ <label for="k12-logo-url" style="margin-top: 16px;">🏛️ School Logo URL (optional)</label>
98
+ <input type="text" id="k12-logo-url" placeholder="https://example.com/school-logo.png"
99
+ class="input-field">
100
+ <small style="color: var(--text-secondary); font-size: 0.8rem;">
101
+ Direct link to school logo image (PNG/JPG)
102
+ </small>
103
  </div>
104
 
105
  <div class="form-group collapsible">
web/static/js/app.js CHANGED
@@ -130,6 +130,10 @@ function initVerifyForm() {
130
  if (emailDomain) {
131
  payload.email_domain = emailDomain;
132
  }
 
 
 
 
133
  }
134
 
135
  const response = await fetch(`${API_BASE}/verify/${currentVerifyType}`, {
@@ -446,7 +450,8 @@ async function previewK12Documents() {
446
  first_name: document.getElementById('first-name').value.trim() || 'John',
447
  last_name: document.getElementById('last-name').value.trim() || 'Doe',
448
  school_name: selectedK12School ? selectedK12School.name : 'Springfield High School',
449
- email_domain: document.getElementById('k12-email-domain').value.trim() || null
 
450
  };
451
 
452
  const response = await fetch(`${API_BASE}/preview/k12`, {
 
130
  if (emailDomain) {
131
  payload.email_domain = emailDomain;
132
  }
133
+ const logoUrl = document.getElementById('k12-logo-url').value.trim();
134
+ if (logoUrl) {
135
+ payload.logo_url = logoUrl;
136
+ }
137
  }
138
 
139
  const response = await fetch(`${API_BASE}/verify/${currentVerifyType}`, {
 
450
  first_name: document.getElementById('first-name').value.trim() || 'John',
451
  last_name: document.getElementById('last-name').value.trim() || 'Doe',
452
  school_name: selectedK12School ? selectedK12School.name : 'Springfield High School',
453
+ email_domain: document.getElementById('k12-email-domain').value.trim() || null,
454
+ logo_url: document.getElementById('k12-logo-url').value.trim() || null
455
  };
456
 
457
  const response = await fetch(`${API_BASE}/preview/k12`, {