WesternFriend/WF-website

View on GitHub
subscription/models.py

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import logging
from typing import Any
from django.shortcuts import redirect

from django.utils import timezone
from django.conf import settings
from django.db import models
from django.http import HttpRequest, HttpResponse
from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField, StreamField
from wagtail import blocks as wagtail_blocks
from wagtail.models import Page

from paypal import blocks as paypal_blocks

from paypal import subscriptions as paypal_subscriptions

logger = logging.getLogger(__name__)


class Subscription(models.Model):
    """A subscription to the magazine."""

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        verbose_name="subscriber",
        editable=True,
        on_delete=models.PROTECT,
        related_name="subscription",
        unique=True,  # Only one subscription per user
    )

    paypal_subscription_id = models.CharField(
        help_text="The PayPal subscription ID. If this field has a value, PayPal will manage the expiration date.",
        max_length=255,
        null=True,
        blank=True,
        unique=True,
    )

    expiration_date = models.DateField(
        help_text="The date the subscription expires. Leave blank for perpetual subscriptions.",
        null=True,
        blank=True,
    )

    class Meta:
        indexes = [
            models.Index(
                fields=["paypal_subscription_id"],
            ),
        ]

    panels = [
        FieldPanel("user"),
        FieldPanel(
            "paypal_subscription_id",
            read_only=True,
        ),
        FieldPanel("expiration_date"),
    ]

    def __str__(self) -> str:
        return f"Subscription {self.id}"  # type: ignore

    @property
    def is_active(self) -> bool:
        """Return whether the subscription is active.

        If the subscription has a PayPal subscription ID, check with
        PayPal.

        Otherwise, if the subscription has an expiration date, check
        that the date is in the future.

        Otherwise, the subscription is perpetually active (e.g., a Board
        member subscription).
        """

        if self.paypal_subscription_id:
            return paypal_subscriptions.subscription_is_active(
                paypal_subscription_id=self.paypal_subscription_id,
            )

        if self.expiration_date is not None:
            expires_in_future = self.expiration_date >= timezone.now().date()
            return expires_in_future

        # Default to inactive for safety
        return False


class SubscriptionIndexPage(Page):
    intro = RichTextField(blank=True)

    body = StreamField(
        [
            (
                "paragraph",
                wagtail_blocks.RichTextBlock(),
            ),
            (
                "paypal_card_row",
                wagtail_blocks.ListBlock(
                    paypal_blocks.PayPalSubscriptionPlanButtonBlock(),
                    template="blocks/blocks/card_row.html",
                ),
            ),
        ],
        blank=True,
        use_json_field=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel("intro"),
        FieldPanel("body"),
    ]

    parent_page_types = ["home.HomePage"]
    subpage_types: list[str] = []

    max_count = 1

    template = "subscription/index.html"

    def get_context(
        self,
        request: HttpRequest,
        *args: tuple,
        **kwargs: dict,
    ) -> dict[str, Any]:
        context = super().get_context(request)

        context["paypal_client_id"] = settings.PAYPAL_CLIENT_ID  # type: ignore

        return context

    def serve(
        self,
        request: HttpRequest,
        *args: tuple,
        **kwargs: dict,
    ) -> HttpResponse:
        # Redirect to the Manage Subscription page
        # if the user is logged in and has a subscription.
        # Superusers should not be redirected.
        if (
            hasattr(request, "user")
            and request.user.is_authenticated
            and request.user.is_subscriber
            and not request.user.is_superuser
        ):
            # redirect to manage subscription page
            manage_subscription_page = ManageSubscriptionPage.objects.first()

            if manage_subscription_page:
                return redirect(
                    manage_subscription_page.url,
                )

        return super().serve(request, *args, **kwargs)


class ManageSubscriptionPage(Page):
    intro = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel("intro"),
    ]

    parent_page_types = ["home.HomePage"]
    subpage_types: list[str] = []

    max_count = 1

    def get_context(
        self,
        request: HttpRequest,
        *args: tuple,
        **kwargs: dict,
    ) -> dict[str, Any]:
        context = super().get_context(request)

        if (
            hasattr(request, "user")
            and request.user.is_authenticated
            and hasattr(request.user, "subscription")
        ):
            context["subscription"] = request.user.subscription

        return context