|
1 | 1 | """Create realistic test data for the sponsor management UI.""" |
2 | 2 |
|
3 | | -from datetime import timedelta |
| 3 | +from datetime import datetime, timedelta |
4 | 4 |
|
5 | 5 | from django.conf import settings |
6 | 6 | from django.contrib.auth import get_user_model |
|
16 | 16 | Sponsorship, |
17 | 17 | SponsorshipBenefit, |
18 | 18 | SponsorshipCurrentYear, |
| 19 | + SponsorshipNotificationLog, |
19 | 20 | SponsorshipPackage, |
20 | 21 | SponsorshipProgram, |
21 | 22 | ) |
@@ -101,9 +102,13 @@ def handle(self, *args, **options): |
101 | 102 | sponsors = self._create_sponsors() |
102 | 103 | created_count = self._create_sponsorships(sponsors, user, current_year, today) |
103 | 104 | self._create_notification_templates() |
| 105 | + notif_count = self._create_notification_logs(user, today) |
104 | 106 |
|
105 | 107 | self.stdout.write( |
106 | | - self.style.SUCCESS(f"Created {created_count} sponsorships across {len(sponsors)} sponsors, years {years}.") |
| 108 | + self.style.SUCCESS( |
| 109 | + f"Created {created_count} sponsorships across {len(sponsors)} sponsors, years {years}. " |
| 110 | + f"{notif_count} notification logs." |
| 111 | + ) |
107 | 112 | ) |
108 | 113 | self.stdout.write("View at: http://localhost:8000/sponsors/manage/sponsorships/") |
109 | 114 |
|
@@ -291,8 +296,117 @@ def _create_notification_templates(self): |
291 | 296 | if created: |
292 | 297 | self.stdout.write(f" Created {created} notification templates.") |
293 | 298 |
|
| 299 | + def _create_notification_logs(self, user, today): |
| 300 | + """Create realistic notification log entries for existing sponsorships.""" |
| 301 | + # Skip if logs already exist for seeded sponsors |
| 302 | + names = [s["name"] for s in SPONSORS] |
| 303 | + if SponsorshipNotificationLog.objects.filter(sponsorship__sponsor__name__in=names).exists(): |
| 304 | + return 0 |
| 305 | + |
| 306 | + messages = [ |
| 307 | + { |
| 308 | + "subject": "Welcome to the PSF Sponsorship Program, {name}!", |
| 309 | + "content": ( |
| 310 | + "Dear {name},\n\n" |
| 311 | + "Thank you for your {level} sponsorship! " |
| 312 | + "Your sponsorship period runs from {start} to {end}.\n\n" |
| 313 | + "We will be in touch shortly with next steps regarding your benefits.\n\n" |
| 314 | + "Best regards,\nPSF Sponsorship Team" |
| 315 | + ), |
| 316 | + "contact_types": "primary, administrative", |
| 317 | + "days_after_applied": 1, |
| 318 | + "statuses": ("approved", "finalized"), |
| 319 | + }, |
| 320 | + { |
| 321 | + "subject": "Action Required: Sponsorship assets needed for {name}", |
| 322 | + "content": ( |
| 323 | + "Dear {name},\n\n" |
| 324 | + "This is a reminder that we still need your sponsorship assets " |
| 325 | + "(logos, descriptions, etc.) for your {level} sponsorship.\n\n" |
| 326 | + "Please submit them at your earliest convenience.\n\n" |
| 327 | + "Best regards,\nPSF Sponsorship Team" |
| 328 | + ), |
| 329 | + "contact_types": "primary", |
| 330 | + "days_after_applied": 15, |
| 331 | + "statuses": ("finalized",), |
| 332 | + }, |
| 333 | + { |
| 334 | + "subject": "Contract Sent: {name} {level} Sponsorship", |
| 335 | + "content": ( |
| 336 | + "Dear {name},\n\n" |
| 337 | + "Please find attached the sponsorship contract for your " |
| 338 | + "{level} sponsorship.\n\n" |
| 339 | + "Please review and sign at your earliest convenience.\n\n" |
| 340 | + "Best regards,\nPSF Sponsorship Team" |
| 341 | + ), |
| 342 | + "contact_types": "primary, administrative, accounting", |
| 343 | + "days_after_applied": 7, |
| 344 | + "statuses": ("approved", "finalized"), |
| 345 | + }, |
| 346 | + { |
| 347 | + "subject": "Sponsorship Finalized: Welcome aboard, {name}!", |
| 348 | + "content": ( |
| 349 | + "Dear {name},\n\n" |
| 350 | + "Great news! Your {level} sponsorship has been finalized. " |
| 351 | + "Your benefits are now active.\n\n" |
| 352 | + "If you have any questions, don't hesitate to reach out.\n\n" |
| 353 | + "Best regards,\nPSF Sponsorship Team" |
| 354 | + ), |
| 355 | + "contact_types": "primary, manager", |
| 356 | + "days_after_applied": 20, |
| 357 | + "statuses": ("finalized",), |
| 358 | + }, |
| 359 | + ] |
| 360 | + |
| 361 | + created = 0 |
| 362 | + sponsorships = Sponsorship.objects.filter( |
| 363 | + sponsor__name__in=names, |
| 364 | + status__in=[Sponsorship.APPROVED, Sponsorship.FINALIZED], |
| 365 | + ).select_related("sponsor", "package") |
| 366 | + |
| 367 | + for sp in sponsorships: |
| 368 | + primary_email = f"contact@{sp.sponsor.name.lower().replace(' ', '')}.com" |
| 369 | + billing_email = f"billing@{sp.sponsor.name.lower().replace(' ', '')}.com" |
| 370 | + |
| 371 | + for msg in messages: |
| 372 | + if sp.status not in msg["statuses"]: |
| 373 | + continue |
| 374 | + |
| 375 | + recipients = primary_email |
| 376 | + if "accounting" in msg["contact_types"] or "administrative" in msg["contact_types"]: |
| 377 | + recipients = f"{primary_email}, {billing_email}" |
| 378 | + |
| 379 | + base_date = sp.applied_on or today |
| 380 | + sent_at = timezone.make_aware( |
| 381 | + datetime.combine(base_date + timedelta(days=msg["days_after_applied"]), datetime.min.time()) |
| 382 | + ) |
| 383 | + |
| 384 | + SponsorshipNotificationLog.objects.create( |
| 385 | + sponsorship=sp, |
| 386 | + subject=msg["subject"].format( |
| 387 | + name=sp.sponsor.name, |
| 388 | + level=sp.level_name, |
| 389 | + ), |
| 390 | + content=msg["content"].format( |
| 391 | + name=sp.sponsor.name, |
| 392 | + level=sp.level_name, |
| 393 | + start=sp.start_date or "TBD", |
| 394 | + end=sp.end_date or "TBD", |
| 395 | + ), |
| 396 | + recipients=recipients, |
| 397 | + contact_types=msg["contact_types"], |
| 398 | + sent_by=user, |
| 399 | + sent_at=sent_at, |
| 400 | + ) |
| 401 | + created += 1 |
| 402 | + |
| 403 | + if created: |
| 404 | + self.stdout.write(f" Created {created} notification log entries.") |
| 405 | + return created |
| 406 | + |
294 | 407 | def _clean(self): |
295 | 408 | names = [s["name"] for s in SPONSORS] |
| 409 | + SponsorshipNotificationLog.objects.filter(sponsorship__sponsor__name__in=names).delete() |
296 | 410 | Contract.objects.filter(sponsorship__sponsor__name__in=names).delete() |
297 | 411 | Sponsorship.objects.filter(sponsor__name__in=names).delete() |
298 | 412 | Sponsor.objects.filter(name__in=names).delete() |
|
0 commit comments