Initial certificate system
This commit is contained in:
30
backend/app/services/certificate_number.py
Normal file
30
backend/app/services/certificate_number.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import hashlib
|
||||
from datetime import date
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
|
||||
|
||||
def _to_base32ish(value: int, length: int) -> str:
|
||||
chars: list[str] = []
|
||||
base = len(ALPHABET)
|
||||
while value:
|
||||
value, remainder = divmod(value, base)
|
||||
chars.append(ALPHABET[remainder])
|
||||
encoded = "".join(reversed(chars)) or ALPHABET[0]
|
||||
return encoded[-length:].rjust(length, ALPHABET[0])
|
||||
|
||||
|
||||
def _digest_int(payload: str) -> int:
|
||||
digest = hashlib.sha256(payload.encode("utf-8")).digest()
|
||||
return int.from_bytes(digest[:8], "big")
|
||||
|
||||
|
||||
def build_certificate_no(sequence_id: int, project_code: str, issue_date: date | None = None) -> str:
|
||||
year = (issue_date or date.today()).year
|
||||
normalized_project = project_code.strip().upper()
|
||||
seed = f"{settings.certificate_no_secret}:{year}:{normalized_project}:{sequence_id}"
|
||||
short_code = _to_base32ish(_digest_int(seed), 7)
|
||||
check_code = _to_base32ish(_digest_int(f"check:{seed}:{short_code}"), 2)
|
||||
return f"PX{year}-{normalized_project}-{short_code}-{check_code}"
|
||||
Reference in New Issue
Block a user