nordabiz/scripts/send_welcome_activation.py
Maciej Pienczyn fac832c80c
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
feat: welcome activation email for users who never logged in
New email template with friendly tone and green CTA button for first-time
account activation. Script with --dry-run, --test-email, --user-id flags
and 72h token validity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:03:29 +01:00

221 lines
6.9 KiB
Python

"""
Send welcome activation emails to users who never logged in.
These users were imported (batch import, admin panel, WhatsApp registration)
with passwords but never received welcome emails or password reset links.
This script generates reset tokens (72h validity) and sends a welcoming
activation email with a link to set their password.
Usage:
# Preview who would receive emails (no sending)
python3 send_welcome_activation.py --dry-run
# Send test email to admin
python3 send_welcome_activation.py --test-email maciej.pienczyn@inpi.pl
# Send to a specific user by ID
python3 send_welcome_activation.py --user-id 53
# Send to all never-logged-in users (requires confirmation)
python3 send_welcome_activation.py --send-all
Run on production server with DATABASE_URL set.
"""
import os
import sys
import argparse
import secrets
from datetime import datetime, timedelta
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from database import SessionLocal, User
import email_service
TOKEN_VALIDITY_HOURS = 72
BASE_URL = 'https://nordabiznes.pl'
def get_never_logged_in_users(db):
"""Find all active, verified users who never logged in."""
return (
db.query(User)
.filter(
User.last_login.is_(None),
User.is_active.is_(True),
)
.order_by(User.id)
.all()
)
def send_activation_email(db, user):
"""Generate token and send welcome activation email to a user."""
token = secrets.token_urlsafe(32)
expires = datetime.now() + timedelta(hours=TOKEN_VALIDITY_HOURS)
user.reset_token = token
user.reset_token_expires = expires
db.commit()
reset_url = f"{BASE_URL}/reset-password/{token}"
success = email_service.send_welcome_activation_email(
email=user.email,
name=user.name,
reset_url=reset_url
)
return success
def cmd_dry_run(db):
"""Show users who would receive activation emails."""
users = get_never_logged_in_users(db)
if not users:
print("Brak użytkowników do aktywacji (wszyscy się zalogowali).")
return
print(f"Użytkownicy, którzy nigdy się nie zalogowali ({len(users)}):\n")
print(f"{'ID':>4} {'Imię i nazwisko':<30} {'Email':<40} {'Aktywny':>7} {'Zweryfikowany':>13}")
print("-" * 100)
for u in users:
print(f"{u.id:>4} {u.name:<30} {u.email:<40} {'tak' if u.is_active else 'nie':>7} {'tak' if u.is_verified else 'nie':>13}")
print(f"\nRazem: {len(users)} użytkowników")
print("\nAby wysłać e-mail testowy: --test-email <adres>")
print("Aby wysłać do jednej osoby: --user-id <ID>")
def cmd_test_email(db, test_email):
"""Send a test activation email to specified address."""
# Find or create a fake context for the test
print(f"Wysyłanie testowego e-maila powitalnego do: {test_email}")
token = secrets.token_urlsafe(32)
reset_url = f"{BASE_URL}/reset-password/{token}"
# Use test data
success = email_service.send_welcome_activation_email(
email=test_email,
name="Testowy Użytkownik",
reset_url=reset_url
)
if success:
print(f" OK: E-mail testowy wysłany do {test_email}")
print(f" Link (nieaktywny - testowy token): {reset_url}")
else:
print(f" FAIL: Nie udało się wysłać e-maila do {test_email}")
sys.exit(1)
def cmd_send_user(db, user_id):
"""Send activation email to a specific user."""
user = db.query(User).get(user_id)
if not user:
print(f"ERROR: Nie znaleziono użytkownika o ID {user_id}")
sys.exit(1)
if not user.is_active:
print(f"ERROR: Użytkownik {user.name} (ID {user_id}) jest nieaktywny")
sys.exit(1)
if user.last_login is not None:
print(f"SKIP: Użytkownik {user.name} (ID {user_id}) już się logował ({user.last_login})")
return
print(f"Wysyłanie e-maila powitalnego do: {user.name} <{user.email}> (ID {user_id})")
success = send_activation_email(db, user)
if success:
print(f" OK: E-mail wysłany do {user.name} <{user.email}>")
else:
print(f" FAIL: Nie udało się wysłać e-maila do {user.name} <{user.email}>")
sys.exit(1)
def cmd_send_all(db):
"""Send activation emails to all never-logged-in users (with confirmation)."""
users = get_never_logged_in_users(db)
if not users:
print("Brak użytkowników do aktywacji.")
return
print(f"Wysyłka do {len(users)} użytkowników:\n")
for u in users:
print(f" {u.id:>4} {u.name:<30} {u.email}")
confirm = input(f"\nCzy wysłać e-maile do {len(users)} użytkowników? (tak/nie): ")
if confirm.strip().lower() != 'tak':
print("Anulowano.")
return
results = {'sent': [], 'failed': []}
for user in users:
try:
success = send_activation_email(db, user)
if success:
results['sent'].append(f"{user.name} <{user.email}>")
print(f" OK: {user.name} <{user.email}>")
else:
results['failed'].append(f"{user.name} <{user.email}>")
print(f" FAIL: {user.name} <{user.email}>")
except Exception as e:
results['failed'].append(f"{user.name} <{user.email}>: {e}")
print(f" ERROR: {user.name} <{user.email}>: {e}")
print(f"\n{'='*50}")
print(f"Wysłano: {len(results['sent'])}")
print(f"Błędy: {len(results['failed'])}")
if results['failed']:
print("\nSzczegóły błędów:")
for f in results['failed']:
print(f" - {f}")
def main():
parser = argparse.ArgumentParser(
description='Wyślij e-maile powitalne do użytkowników, którzy nigdy się nie zalogowali.'
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--dry-run', action='store_true',
help='Pokaż listę użytkowników bez wysyłania e-maili')
group.add_argument('--test-email', type=str,
help='Wyślij testowy e-mail na podany adres')
group.add_argument('--user-id', type=int,
help='Wyślij e-mail do użytkownika o podanym ID')
group.add_argument('--send-all', action='store_true',
help='Wyślij e-maile do wszystkich niezalogowanych (wymaga potwierdzenia)')
args = parser.parse_args()
if not args.dry_run and not email_service.is_configured():
print("ERROR: Email service not configured. Set Azure credentials in .env")
sys.exit(1)
db = SessionLocal()
try:
if args.dry_run:
cmd_dry_run(db)
elif args.test_email:
cmd_test_email(db, args.test_email)
elif args.user_id:
cmd_send_user(db, args.user_id)
elif args.send_all:
cmd_send_all(db)
finally:
db.close()
if __name__ == '__main__':
main()