示例#1
0
class SMSMessage(UUIDModel, TimestampModel):
    phone = models.ForeignKey(null=True,
                              on_delete=models.deletion.SET_NULL,
                              to="smsbot.Number")
    direction = TurnoutEnumField(MessageDirectionType)
    message = models.TextField(blank=True, null=True)
    status = TurnoutEnumField(SMSDeliveryStatus, null=True)
    status_time = models.DateTimeField(null=True)
    twilio_sid = models.TextField(null=True, db_index=True)
    kwargs = JSONField(null=True)
    blast = models.ForeignKey(null=True,
                              on_delete=models.deletion.SET_NULL,
                              to="smsbot.Blast")

    class Meta(object):
        ordering = ["-created_at"]

    def __str__(self):
        return f"SMSMessage - {self.phone_id} {self.direction}"

    def delivery_status_webhook(self):
        if settings.PRIMARY_ORIGIN.startswith("https"):
            return f"{settings.PRIMARY_ORIGIN}/v1/smsbot/twilio-message-status/{self.uuid}/"
        else:
            # twilio rejects http webhooks
            return None
示例#2
0
class Report(SubscriberModel, UUIDModel, TimestampModel):
    author = models.ForeignKey("accounts.User",
                               null=True,
                               on_delete=models.PROTECT)
    status = TurnoutEnumField(enums.ReportStatus,
                              null=True,
                              default=enums.ReportStatus.PENDING)
    type = TurnoutEnumField(enums.ReportType, null=True)
    result_item = models.ForeignKey("storage.StorageItem",
                                    null=True,
                                    on_delete=models.SET_NULL,
                                    blank=True)
示例#3
0
class Lookup(
        ActionModel,
        SubscriberModel,
        TrackingModel,
        SearchableModel("email", "last_name", "first_name"),  # type: ignore
        UUIDModel,
        TimestampModel,
):
    person = models.ForeignKey("people.Person",
                               null=True,
                               on_delete=models.PROTECT)

    first_name = models.TextField()
    last_name = models.TextField()
    state = models.ForeignKey("election.State", on_delete=models.PROTECT)
    registered = models.BooleanField(null=True)
    voter_status = TurnoutEnumField(enums.VoterStatus, null=True)
    too_many = models.BooleanField(null=True)
    last_updated = models.DateTimeField(null=True, blank=True)
    response = JSONField(null=True)
    alloy_response = JSONField(null=True)
    alloy_status = TurnoutEnumField(enums.VoterStatus, null=True)
    targetsmart_response = JSONField(null=True)
    targetsmart_status = TurnoutEnumField(enums.VoterStatus, null=True)

    date_of_birth = models.DateField(null=True, blank=True)
    unparsed_full_address = models.TextField(null=True, blank=True)
    address1 = models.TextField(null=True, blank=True)
    address2 = models.TextField(null=True, blank=True)
    city = models.TextField(null=True, blank=True)
    zipcode = models.TextField(null=True,
                               blank=True,
                               validators=[zip_validator])
    age = models.IntegerField(null=True, blank=True)
    gender = TurnoutEnumField(enums.TargetSmartGender, null=True)
    phone = PhoneNumberField(null=True, blank=True, db_index=True)
    email = models.EmailField(null=True, blank=True)
    sms_opt_in = models.BooleanField(null=True, blank=True, default=None)

    class Meta(object):
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["created_at"]),
        ]

    def __str__(self):
        return f"Verification - {self.first_name} {self.last_name}, {self.state.pk}".strip(
        )
示例#4
0
class Event(UUIDModel, TimestampModel):
    action = models.ForeignKey("action.Action", on_delete=models.PROTECT, db_index=True)
    event_type = TurnoutEnumField(enums.EventType, db_index=True)

    class Meta:
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["event_type", "created_at"]),
        ]

    def check_hooks(self, tool):
        from absentee.event_hooks import absentee_hooks
        from register.event_hooks import register_hooks
        from verifier.event_hooks import verifier_hooks

        # Tool-specific hooks
        if tool == enums.ToolName.VERIFY.value:
            hook = verifier_hooks.get(self.event_type)
            if hook:
                hook(
                    self.action_id, self.uuid,
                )
        if tool == enums.ToolName.REGISTER.value:
            hook = register_hooks.get(self.event_type)
            if hook:
                hook(
                    self.action_id, self.uuid,
                )
        if tool == enums.ToolName.ABSENTEE.value:
            hook = absentee_hooks.get(self.event_type)
            if hook:
                hook(
                    self.action_id, self.uuid,
                )
示例#5
0
class Subscription(UUIDModel, TimestampModel):
    subscriber = models.OneToOneField("multi_tenant.Client",
                                      on_delete=models.PROTECT)
    interest = models.ForeignKey(Interest,
                                 null=True,
                                 blank=True,
                                 on_delete=models.SET_NULL)
    expires = models.DateField(null=True, blank=True, db_index=True)
    plan = TurnoutEnumField(
        enums.SubscriberPlan,
        default=enums.SubscriberPlan.FREE,
        null=True,
    )

    primary_contact_first_name = models.TextField(null=True)
    primary_contact_last_name = models.TextField(null=True)
    primary_contact_email = models.EmailField(null=True)
    primary_contact_phone = PhoneNumberField(blank=True, null=True)
    ein = models.TextField(null=True, blank=True)

    internal_notes = models.TextField(null=True, blank=True)

    # DEPRECATED
    product = models.ForeignKey(Product,
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)

    def __str__(self):
        return f"{self.subscriber} {self.expires}"
示例#6
0
class SubscriberIntegrationProperty(UUIDModel, TimestampModel):
    subscriber = models.ForeignKey(Client, on_delete=models.CASCADE,)
    external_tool = TurnoutEnumField(enums.ExternalToolType, null=True)
    name = models.TextField(null=True)
    value = models.TextField(null=True)

    class Meta:
        ordering = ["-created_at"]
示例#7
0
class SecureUploadItem(UUIDModel, TimestampModel):
    file = models.FileField(storage=HighValueStorage())
    upload_type = TurnoutEnumField(enums.SecureUploadType)
    content_type = models.TextField()
    purged = models.DateTimeField(blank=True, null=True)

    class Meta:
        ordering = ["-created_at"]
示例#8
0
class UpdateNotificationWebhook(UUIDModel, TimestampModel):
    name = models.TextField(null=True)
    type = TurnoutEnumField(enums.NotificationWebhookTypes, null=True)
    trigger_url = models.URLField(null=True, max_length=200)
    last_triggered = models.DateTimeField(null=True, blank=True)
    active = models.BooleanField(null=True, default=True)

    objects = UpdateNotificationWebhookManager()

    def __str__(self):
        return self.name
示例#9
0
class Proxy(UUIDModel, TimestampModel):
    address = models.TextField(null=True)
    description = models.TextField(null=True)
    state = TurnoutEnumField(enums.ProxyStatus, null=True)
    failure_count = models.IntegerField(null=True)
    last_used = models.DateTimeField(null=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"Proxy {self.uuid} - {self.address} {self.state}"
示例#10
0
class LeoContactOverride(TimestampModel):
    region = models.OneToOneField("official.Region",
                                  on_delete=models.CASCADE,
                                  primary_key=True)
    submission_method = TurnoutEnumField(enums.SubmissionType, null=True)
    email = models.EmailField(null=True)
    phone = PhoneNumberField(null=True)
    fax = PhoneNumberField(null=True)
    notes = models.TextField(null=True, blank=True)

    class Meta:
        ordering = ["region__state__code", "region__name"]
示例#11
0
class Link(UUIDModel, TimestampModel):
    action = models.ForeignKey("action.Action",
                               null=True,
                               on_delete=models.CASCADE)
    subscriber = models.ForeignKey("multi_tenant.Client",
                                   null=True,
                                   on_delete=models.CASCADE)
    external_tool = TurnoutEnumField(enums.ExternalToolType, null=True)
    external_id = models.TextField(null=True)

    class Meta:
        ordering = ["-created_at"]
示例#12
0
class StateInformationFieldType(UUIDModel, TimestampModel):
    slug = models.SlugField("Name", max_length=50, unique=True)
    long_name = models.CharField("Long Name", max_length=200)
    help_text = models.TextField("Help Text",
                                 help_text="Markdown allowed",
                                 null=True,
                                 blank=True)
    field_format = TurnoutEnumField(enums.StateFieldFormats,
                                    null=True,
                                    default=enums.StateFieldFormats.MARKDOWN)

    class Meta(object):
        ordering = ["slug"]

    def __str__(self):
        return self.slug
示例#13
0
class RegionEsignMethod(models.Model):
    region = models.OneToOneField(
        "official.Region",
        on_delete=models.DO_NOTHING,
        primary_key=True,
        related_name="esign",
    )

    state = models.ForeignKey("election.State", on_delete=models.DO_NOTHING)

    has_override = models.BooleanField()
    submission_method = TurnoutEnumField(enums.SubmissionType)

    class Meta:
        managed = False
        ordering = ["state_id", "region__name"]
示例#14
0
class Fax(UUIDModel, TimestampModel):
    token = models.UUIDField(default=uuid.uuid4)
    storage_item = models.ForeignKey(
        "storage.StorageItem",
        on_delete=models.PROTECT,
    )

    status = TurnoutEnumField(enums.FaxStatus)
    status_message = models.TextField(null=True)
    status_timestamp = models.DateTimeField(null=True)

    on_complete_task = models.TextField(null=True)
    on_complete_task_arg = models.TextField(null=True)

    to = PhoneNumberField(null=True)

    class Meta:
        ordering = ["-created_at"]

    def validate_token(self, token):
        return token == str(self.token)
示例#15
0
class Phone(UUIDModel, TimestampModel):
    phone_type = TurnoutEnumField(PhoneType, db_index=True)
    phone_number = PhoneNumberField()
示例#16
0
class Interest(
        SearchableModel("organization_name", "first_name", "last_name",
                        "email"),  # type: ignore
        UUIDModel,
        TimestampModel,
):
    organization_name = models.TextField(null=True)
    website = models.URLField(null=True)
    first_name = models.TextField(null=True)
    last_name = models.TextField(null=True)
    email = models.EmailField(null=True)
    phone = PhoneNumberField(blank=True, null=True)
    plan = TurnoutEnumField(
        enums.SubscriberPlan,
        default=enums.SubscriberPlan.FREE,
        null=True,
    )
    ein = models.TextField(null=True, blank=True)
    status = TurnoutEnumField(
        enums.SubscriptionInterestStatus,
        default=enums.SubscriptionInterestStatus.PENDING,
        null=True,
    )
    reject_reason = models.TextField(null=True)
    sms_mode = TurnoutEnumField(
        enums.SubscriberSMSOption,
        default=enums.SubscriberSMSOption.BOX_UNCHECKED,
        null=True,
    )

    # DEPRECATED
    nonprofit = models.BooleanField(null=True)
    product = models.ForeignKey(Product, null=True, on_delete=models.SET_NULL)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.organization_name} Interest Submission"

    @property
    def consumed(self):
        return self.status == enums.SubscriptionInterestStatus.SUBSCRIBED

    def consume(self, subscriber: "Client", initial_user_email: str,
                slug: str) -> None:
        from multi_tenant.models import SubscriberSlug

        self.status = enums.SubscriptionInterestStatus.SUBSCRIBED
        self.save()
        subscriber_slug = SubscriberSlug.objects.create(slug=slug,
                                                        subscriber=subscriber)
        subscriber.default_slug = subscriber_slug
        subscriber.set_sms_mode(str(self.sms_mode))
        subscriber.save()
        sub = Subscription.objects.create(
            subscriber=subscriber,
            product=self.product,
            plan=self.plan,
            primary_contact_first_name=self.first_name,
            primary_contact_last_name=self.last_name,
            primary_contact_email=self.email,
            primary_contact_phone=self.phone,
            ein=self.ein,
            interest=self,
        )

        invite_user(initial_user_email, subscriber)
        send_organization_welcome_notification.delay(subscriber.pk,
                                                     initial_user_email)
        sync_subscriber_to_actionnetwork.delay(subscriber.pk)
示例#17
0
class StorageItem(SubscriberModel, UUIDModel, TimestampModel):
    token = SmallUUIDField(default=uuid_default())
    app = TurnoutEnumField(enums.FileType)
    file = models.FileField(storage=HighValueDownloadStorage())
    email = models.EmailField(blank=True, null=True)
    expires = models.DateTimeField(default=storage_expire_date_time)
    first_download = models.DateTimeField(blank=True, null=True)
    purged = models.DateTimeField(blank=True, null=True)
    preview_of = models.ForeignKey("self",
                                   on_delete=models.CASCADE,
                                   blank=True,
                                   null=True,
                                   db_index=True)

    class Meta:
        ordering = ["-created_at"]

    def get_absolute_url(self):
        return self.file.url

    def refresh_token(self):
        self.token = smalluuid.SmallUUID()
        self.expires = storage_expire_date_time()
        self.save(update_fields=["token", "expires"])
        return self.token

    def validate_token(self, token):
        return (token == str(self.token)) and (
            datetime.datetime.now(tz=datetime.timezone.utc) < self.expires)

    @property
    def download_url(self):
        path = reverse("storage:download", kwargs={"pk": self.pk})
        return f"{settings.PRIMARY_ORIGIN}{path}?token={self.token}"

    @property
    def reset_url(self):
        return settings.FILE_TOKEN_RESET_URL.format(item_id=self.pk)

    @property
    def purged_url(self):
        return settings.FILE_TOKEN_PURGED_URL.format(
            item_id=self.pk,
            filetype=self.app,
            purge_days=settings.FILE_PURGE_DAYS)

    def track_download(self):
        if self.app == enums.FileType.REGISTRATION_FORM:
            try:
                registration = Registration.objects.get(result_item=self)
            except Registration.DoesNotExist:
                # we briefly linked to lob PDFs to download, but
                # stopped.  some links are still in the wild.
                registration = Registration.objects.get(result_item_mail=self)
            registration.action.track_event(enums.EventType.DOWNLOAD)
        if self.app == enums.FileType.ABSENTEE_REQUEST_FORM:
            ballot_request = BallotRequest.objects.get(result_item=self)
            ballot_request.action.track_event(enums.EventType.DOWNLOAD)
        if not self.first_download:
            self.first_download = now()
            self.save(update_fields=["first_download"])
示例#18
0
class Client(UUIDModel, TimestampModel):
    name = models.CharField(max_length=200)
    url = models.URLField(verbose_name="Homepage URL")
    email = models.EmailField(
        verbose_name="Default 'From' Email Address",
        max_length=254,
        null=True,
        default="*****@*****.**",
    )
    privacy_policy = models.URLField(
        verbose_name="Privacy Policy URL", null=True, blank=True
    )
    terms_of_service = models.URLField(
        verbose_name="Terms of Service URL", null=True, blank=True
    )
    sms_enabled = models.BooleanField(
        verbose_name="SMS Program Enabled", default=True, null=True
    )
    sms_checkbox = models.BooleanField(
        verbose_name="SMS Checkbox Enabled", default=True, null=True
    )
    sms_checkbox_default = models.BooleanField(
        verbose_name="SMS Checkbox Checked By Default", default=False, null=True
    )
    sms_disclaimer = models.TextField(
        verbose_name="Custom SMS Disclaimer", blank=True, null=True
    )
    # In order to create this in the admin, we need blank=True
    default_slug = models.ForeignKey(
        "multi_tenant.SubscriberSlug", null=True, on_delete=models.PROTECT, blank=True
    )
    status = TurnoutEnumField(
        enums.SubscriberStatus, default=enums.SubscriberStatus.ACTIVE, null=True
    )

    # Passed to Civis to determine if subscriber's data should be synced to TMC
    sync_tmc = models.BooleanField(
        verbose_name="TMC Sync Enabled", default=False, null=True
    )
    sync_bluelink = models.BooleanField(
        verbose_name="Bluelink Sync Enabled", default=False, null=True
    )

    # Determines if we show our own donate asks when a user is interacting with
    # this client.
    is_first_party = models.BooleanField(default=False)

    has_api_access = models.BooleanField(default=False, null=True)

    class Meta:
        ordering = ["created_at"]
        verbose_name = "Subscriber"
        verbose_name_plural = "Subscribers"

    def __str__(self):
        return self.name

    @cached_property
    def slug(self):
        return self.default_slug.slug

    @property
    def full_email_address(self) -> str:
        if self.is_first_party:
            return f'"VoteAmerica" <*****@*****.**>'
        else:
            clean_name = self.name.replace('"', "'")
            return f'"{clean_name}" <{self.email}>'

    @property
    def transactional_from_email_address(self) -> str:
        if self.is_first_party:
            return self.full_email_address
        clean_name = self.name.replace('"', "'")
        return f'"{clean_name} via VoteAmerica" <{settings.DEFAULT_EMAIL_FROM}>'

    @property
    def transactional_from_name(self) -> str:
        if self.is_first_party:
            return "VoteAmerica"
        else:
            return f"{self.name} via VoteAmerica"

    @property
    def stats(self):
        refresh_obj = StatsRefresh.objects.all()[0]

        stats = {
            "last_updated": refresh_obj.last_run,
            "register": 0,
            "verify": 0,
            "absentee": 0,
            "locate": 0,
        }

        for stat in self.subscriberstats_set.all():
            stats[stat.tool.value] = stat.count

        return stats

    @cached_property
    def disclaimer(self):
        return render_to_string(DISCLAIMER_TEMPLATE, {"subscriber": self}).replace(
            "\n", ""
        )

    @cached_property
    def plan(self):
        if hasattr(self, "subscription"):
            return self.subscription.plan
        else:
            # System subscribers -- like VoteAmerica, and the dev Default one,
            # don't have subscriptions.
            return enums.SubscriberPlan.PREMIUM

    def plan_has_data_access(self):
        return self.plan != enums.SubscriberPlan.FREE

    def get_sms_mode(self):
        if not self.sms_enabled:
            return enums.SubscriberSMSOption.NONE
        if not self.sms_checkbox:
            return enums.SubscriberSMSOption.AUTO_OPT_IN
        if self.sms_checkbox_default:
            return enums.SubscriberSMSOption.BOX_CHECKED
        else:
            return enums.SubscriberSMSOption.BOX_UNCHECKED

    def set_sms_mode(self, opt):
        if opt == str(enums.SubscriberSMSOption.NONE):
            self.sms_enabled = False
            self.sms_checkbox = False
            self.sms_checkbox_default = False
        elif opt == str(enums.SubscriberSMSOption.AUTO_OPT_IN):
            self.sms_enabled = True
            self.sms_checkbox = False
            self.sms_checkbox_default = False
        elif opt == str(enums.SubscriberSMSOption.BOX_UNCHECKED):
            self.sms_enabled = True
            self.sms_checkbox = True
            self.sms_checkbox_default = False
        elif opt == str(enums.SubscriberSMSOption.BOX_CHECKED):
            self.sms_enabled = True
            self.sms_checkbox = True
            self.sms_checkbox_default = True
示例#19
0
class Blast(TimestampModel, UUIDModel):
    description = models.CharField(max_length=100, null=True)
    content = models.TextField(null=True)
    sql = models.TextField(null=True)
    campaign = models.CharField(max_length=100, null=True)
    blast_type = TurnoutEnumField(BlastType, null=True)

    class Meta(object):
        ordering = ["-created_at"]

    def __str__(self):
        return f"Blast - {self.description} - {self.blast_type} ({self.uuid})"

    @tracer.wrap()
    def count(self):
        with connections["readonly"].cursor() as cursor:
            cursor.execute(
                f"SELECT count(*) FROM ({self.sql.format(blast_id=str(self.uuid),)}) a"
            )
            return cursor.fetchall()[0][0]

    @tracer.wrap()
    def enqueue(self, test_phone: str = None):
        from .tasks import trigger_blast_sms, trigger_blast_mms_map

        if self.blast_type == BlastType.SMS and test_phone:
            trigger_blast_sms.delay(self.pk, test_phone, force_dup=True)
            return

        with connections["readonly"].cursor() as cursor:
            cursor.execute(self.sql.format(blast_id=str(self.uuid), ))
            columns = [col[0] for col in cursor.description]
            if test_phone:
                logger.info(
                    f"Enqueueing test message for {self} to {test_phone}")
            else:
                logger.info(
                    f"Enqueueing {cursor.rowcount} messages for {self}")
            for row in cursor.fetchall():
                r = dict(zip(columns, row))
                if self.blast_type == BlastType.SMS:
                    trigger_blast_sms.delay(self.pk,
                                            r["phone"])  # type: ignore
                elif self.blast_type == BlastType.MMS_MAP:
                    if test_phone:
                        trigger_blast_mms_map.delay(
                            self.pk,
                            test_phone,
                            r["map_type"],  # type: ignore
                            r["address_full"],  # type: ignore
                            force_dup=True,
                        )
                    else:
                        trigger_blast_mms_map.delay(
                            self.pk,
                            r["phone"],
                            r["map_type"],
                            r["address_full"]  # type: ignore
                        )
                else:
                    logger.error(
                        f"unrecognized blast_type {self.blast_type} for {self}"
                    )
                if test_phone:
                    break
            if not test_phone:
                logger.info(
                    f"Done enqueueing {cursor.rowcount} messages for {self}")
示例#20
0
class SubscriberStats(SubscriberModel, UUIDModel):
    tool = TurnoutEnumField(enums.ToolName)
    count = models.BigIntegerField()

    class Meta:
        unique_together = (("subscriber", "tool"), )
示例#21
0
class BallotRequest(
        ActionModel,
        SubscriberModel,
        TrackingModel,
        SearchableModel("email", "last_name", "first_name"),  # type: ignore
        UUIDModel,
        TimestampModel,
):
    first_name = models.TextField(null=True)
    middle_name = models.TextField(null=True, blank=True)
    last_name = models.TextField(null=True)
    suffix = models.TextField(null=True, blank=True)
    date_of_birth = models.DateField(null=True)
    email = models.EmailField(null=True)
    phone = PhoneNumberField(null=True, blank=True, db_index=True)
    address1 = models.TextField(null=True)
    address2 = models.TextField(null=True, blank=True)
    city = models.TextField(null=True)
    state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="absentee_primary",
        null=True,
    )
    zipcode = models.TextField(null=True, validators=[zip_validator])
    deliverable = models.BooleanField(null=True)

    mailing_address1 = models.TextField(null=True, blank=True)
    mailing_address2 = models.TextField(null=True, blank=True)
    mailing_city = models.TextField(null=True, blank=True)
    mailing_state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="absentee_mailing",
        null=True,
    )
    mailing_zipcode = models.TextField(null=True,
                                       validators=[zip_validator],
                                       blank=True)
    mailing_deliverable = models.BooleanField(null=True)

    request_mailing_address1 = models.TextField(null=True, blank=True)
    request_mailing_address2 = models.TextField(null=True, blank=True)
    request_mailing_city = models.TextField(null=True, blank=True)
    request_mailing_state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="absentee_request_mailing",
        null=True,
    )
    request_mailing_zipcode = models.TextField(null=True,
                                               validators=[zip_validator],
                                               blank=True)
    request_mailing_deliverable = models.BooleanField(null=True)

    us_citizen = models.BooleanField(null=True, default=False)
    sms_opt_in = models.BooleanField(null=True, default=False)

    state_fields = JSONField(null=True)

    status = TurnoutEnumField(
        enums.TurnoutActionStatus,
        default=enums.TurnoutActionStatus.PENDING,
        null=True,
    )

    region = models.ForeignKey("official.Region",
                               null=True,
                               on_delete=models.SET_NULL)

    result_item = models.ForeignKey("storage.StorageItem",
                                    null=True,
                                    on_delete=models.SET_NULL)
    result_item_mail = models.ForeignKey(
        "storage.StorageItem",
        null=True,
        on_delete=models.SET_NULL,
        related_name="absentee_mail",
    )

    signature = models.ForeignKey("storage.SecureUploadItem",
                                  null=True,
                                  on_delete=models.SET_NULL)

    submit_date = models.DateField(null=True)

    esign_method = TurnoutEnumField(enums.SubmissionType, null=True)

    referring_tool = TurnoutEnumField(enums.ToolName, null=True, blank=True)

    class Meta:
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["created_at"]),
        ]

    def __str__(self):
        return f"Absentee - {self.first_name} {self.last_name}, {self.state.pk}".strip(
        )

    @property
    def full_name(self):
        if self.middle_name:
            return f"{self.first_name} {self.middle_name} {self.last_name}"
        return f"{self.first_name} {self.last_name}"
示例#22
0
class Registration(
        ActionModel,
        SubscriberModel,
        TrackingModel,
        SearchableModel("email", "last_name", "first_name"),  # type: ignore
        UUIDModel,
        TimestampModel,
):
    title = TurnoutEnumField(enums.PersonTitle, null=True)
    first_name = models.TextField(null=True)
    middle_name = models.TextField(null=True)
    last_name = models.TextField(null=True)
    suffix = models.TextField(null=True)
    date_of_birth = models.DateField(null=True)
    email = models.EmailField(null=True)
    phone = PhoneNumberField(null=True, db_index=True)
    address1 = models.TextField(null=True)
    address2 = models.TextField(null=True, blank=True)
    city = models.TextField(null=True)
    state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="registration_primary",
        null=True,
    )
    zipcode = models.TextField(null=True, validators=[zip_validator])
    deliverable = models.BooleanField(null=True)

    previous_title = TurnoutEnumField(enums.PersonTitle, null=True, blank=True)
    previous_first_name = models.TextField(null=True, blank=True)
    previous_middle_name = models.TextField(null=True, blank=True)
    previous_last_name = models.TextField(null=True, blank=True)
    previous_suffix = models.TextField(null=True, blank=True)

    previous_address1 = models.TextField(null=True, blank=True)
    previous_address2 = models.TextField(null=True, blank=True)
    previous_city = models.TextField(null=True, blank=True)
    previous_state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="registration_previous",
        null=True,
    )
    previous_zipcode = models.TextField(null=True,
                                        validators=[zip_validator],
                                        blank=True)

    mailing_address1 = models.TextField(null=True, blank=True)
    mailing_address2 = models.TextField(null=True, blank=True)
    mailing_city = models.TextField(null=True, blank=True)
    mailing_state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="registration_mailing",
        null=True,
    )
    mailing_zipcode = models.TextField(null=True,
                                       validators=[zip_validator],
                                       blank=True)
    mailing_deliverable = models.BooleanField(null=True)

    request_mailing_address1 = models.TextField(null=True, blank=True)
    request_mailing_address2 = models.TextField(null=True, blank=True)
    request_mailing_city = models.TextField(null=True, blank=True)
    request_mailing_state = models.ForeignKey(
        "election.State",
        on_delete=models.PROTECT,
        related_name="registration_request_mailing",
        null=True,
    )
    request_mailing_zipcode = models.TextField(null=True,
                                               validators=[zip_validator],
                                               blank=True)
    request_mailing_deliverable = models.BooleanField(null=True)

    gender = TurnoutEnumField(enums.RegistrationGender, null=True)
    race_ethnicity = TurnoutEnumField(enums.RaceEthnicity, null=True)
    party = TurnoutEnumField(enums.PoliticalParties, null=True)

    us_citizen = models.BooleanField(null=True, default=False)
    sms_opt_in = models.BooleanField(null=True, blank=True, default=None)

    status = TurnoutEnumField(
        enums.TurnoutActionStatus,
        default=enums.TurnoutActionStatus.PENDING,
        null=True,
    )

    result_item = models.ForeignKey("storage.StorageItem",
                                    null=True,
                                    on_delete=models.SET_NULL)
    result_item_mail = models.ForeignKey(
        "storage.StorageItem",
        null=True,
        on_delete=models.SET_NULL,
        related_name="registration_mail",
    )

    referring_tool = TurnoutEnumField(enums.ToolName, null=True, blank=True)

    state_fields = JSONField(null=True, blank=True)
    state_api_result = JSONField(null=True, blank=True)

    custom_ovr_link = models.TextField(null=True, blank=True)

    matched_region = models.ForeignKey("official.Region",
                                       null=True,
                                       on_delete=models.SET_NULL,
                                       related_name="+")
    region = models.ForeignKey("official.Region",
                               null=True,
                               on_delete=models.SET_NULL,
                               related_name="+")

    class Meta:
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["created_at"]),
        ]

    def __str__(self):
        return f"Registration - {self.first_name} {self.last_name}, {self.state.pk}".strip(
        )

    @property
    def full_name(self):
        if self.middle_name:
            return f"{self.first_name} {self.middle_name} {self.last_name}"
        return f"{self.first_name} {self.last_name}"