import re from django.conf import settings from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ from django_tidb.fields import BigAutoRandomField EXAM_VOUCHER_PATTERN = re.compile(r"^EXAM-[A-Z0-9]{5}(-[A-Z0-9]{5}){4}$") def exam_voucher_code_validator(value: str) -> None: if not EXAM_VOUCHER_PATTERN.match(value): raise ValidationError(_("Invalid exam voucher code format")) class ExamVoucher(models.Model): class UsedChoices(models.TextChoices): N = "N" Y = "Y" id = BigAutoRandomField(primary_key=True) unique_code = models.CharField( max_length=34, unique=True, validators=[exam_voucher_code_validator], ) exam = models.ForeignKey("labs.Exam", on_delete=models.CASCADE) valid_before = models.DateField(null=True, blank=True) used = models.CharField( max_length=1, default=UsedChoices.N, choices=UsedChoices.choices ) redeem_at = models.DateTimeField(null=True, blank=True) redeem_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, null=True, blank=True, ) marketing_code = models.CharField(max_length=100, blank=True, default="") created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "exam_vouchers" indexes = [ models.Index(fields=["exam", "used"], name="idx_ev_exam_used"), ]