Coverage for src/couchers/helpers/postal_verification.py: 100%
11 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
1import secrets
3from sqlalchemy import exists
4from sqlalchemy.orm import Session
6from couchers.constants import (
7 POSTAL_VERIFICATION_CODE_ALPHABET,
8 POSTAL_VERIFICATION_CODE_LENGTH,
9)
10from couchers.models import User
11from couchers.models.postal_verification import PostalVerificationAttempt
12from couchers.sql import couchers_select as select
15def generate_postal_verification_code() -> str:
16 """
17 Generates a random 6-character uppercase alphanumeric code.
18 Uses a reduced alphabet to avoid confusion (no I, O, 0, 1).
19 """
20 return "".join(secrets.choice(POSTAL_VERIFICATION_CODE_ALPHABET) for _ in range(POSTAL_VERIFICATION_CODE_LENGTH))
23def has_postal_verification(session: Session, user: User) -> bool:
24 """
25 Check if user has valid postal verification.
27 Similar to strong verification, we query the database rather than
28 storing a denormalized flag on the user.
29 """
30 return session.execute(
31 select(
32 exists(
33 select(PostalVerificationAttempt)
34 .where(PostalVerificationAttempt.user_id == user.id)
35 .where(PostalVerificationAttempt.is_valid)
36 )
37 )
38 ).scalar()