Skip to content

Commit

Permalink
feat: start can_allocate() implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
iloveagent57 committed Aug 29, 2023
1 parent e97af2f commit 91532f9
Show file tree
Hide file tree
Showing 5 changed files with 322 additions and 10 deletions.
43 changes: 43 additions & 0 deletions enterprise_access/apps/content_assignments/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Primary Python API for interacting with Assignment
records and business logic.
"""
from django.db.models import Sum

from .constants import LearnerContentAssignmentStateChoices
from .models import LearnerContentAssignment


def get_assignments_for_policy(
subsidy_access_policy,
state=LearnerContentAssignmentStateChoices.ALLOCATED,
learner_emails=None,
):
"""
Returns a queryset of all ``LearnerContentAssignment`` records
for the given policy, optionally filtered to only those
associated with the given ``learner_emails``.
"""
queryset = LearnerContentAssignment.objects.select_related(
'assignment_policy',
).filter(
assignment_policy=subsidy_access_policy,
state=state,
)
if learner_emails:
queryset = queryset.filter(
learner_email__in=learner_emails,
)
return queryset


def get_allocated_quantity_for_policy(subsidy_access_policy):
"""
Returns a float representing the total quantity, in USD cents, currently allocated
via Assignments for the given policy.
"""
assignments_queryset = get_assignments_for_policy(subsidy_access_policy)
aggregate = assignments_queryset.aggregate(
total_quantity=Sum('content_quantity'),
)
return aggregate['total_quantity']
4 changes: 1 addition & 3 deletions enterprise_access/apps/content_assignments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from django_extensions.db.models import TimeStampedModel
from simple_history.models import HistoricalRecords

from enterprise_access.apps.subsidy_access_policy.models import SubsidyAccessPolicy

from .constants import LearnerContentAssignmentStateChoices


Expand All @@ -25,7 +23,7 @@ class AssignmentPolicy(TimeStampedModel):
unique=True,
)
subsidy_access_policy = models.ForeignKey(
SubsidyAccessPolicy,
'subsidy_access_policy.SubsidyAccessPolicy',
related_name="assignment_policy",
on_delete=models.CASCADE,
db_index=True,
Expand Down
47 changes: 47 additions & 0 deletions enterprise_access/apps/subsidy_access_policy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from edx_django_utils.cache.utils import get_cache_key

from enterprise_access.apps.api_client.lms_client import LmsApiClient
from enterprise_access.apps.content_assignments import api as assignments_api

from .constants import (
CREDIT_POLICY_TYPE_PRIORITY,
Expand Down Expand Up @@ -809,3 +810,49 @@ def can_redeem(self, lms_user_id, content_key, skip_customer_user_check=False):

def redeem(self, lms_user_id, content_key, all_transactions, metadata=None):
raise NotImplementedError

def can_allocate(self, number_of_learners, content_key, content_price_cents):
"""
"""
# inactive policy
if not self.active:
return (False, REASON_POLICY_EXPIRED)

# no content key in catalog
if not self.catalog_contains_content_key(content_key):
return (False, REASON_CONTENT_NOT_IN_CATALOG)

if not self.is_subsidy_active:
return (False, REASON_SUBSIDY_EXPIRED)

# Determine total cost, in cents, of content to potentially allocated
total_price_cents = number_of_learners * content_price_cents

# Determine total amount, in cents, already transacted via this policy.
# This is a number <= 0
spent_amount_cents = self.aggregates_for_policy().get('total_quantity') or 0

# Determine total amount, in cents, of assignments already
# allocated via this policy. This is a number <= 0
total_allocated_assignments_cents = assignments_api.get_allocated_quantity_for_policy(self)
total_allocated_and_spent_cents = spent_amount_cents + total_allocated_assignments_cents

# Use all of these pieces to ensure that the assignments to potentially
# allocate won't exceed the remaining balance of the related subsidy.
if self.content_would_exceed_limit(
total_allocated_and_spent_cents,
self.subsidy_balance(),
total_price_cents,
):
return (False, REASON_NOT_ENOUGH_VALUE_IN_SUBSIDY)

# Lastly, use all of these pieces to ensure that the assignments to potentially
# allocate won't exceed the spend limit of this policy
if self.content_would_exceed_limit(
total_allocated_and_spent_cents,
self.spend_limit,
total_price_cents,
):
return (False, REASON_POLICY_SPEND_LIMIT_REACHED)

return (True, None)
14 changes: 14 additions & 0 deletions enterprise_access/apps/subsidy_access_policy/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from enterprise_access.apps.subsidy_access_policy.constants import AccessMethods
from enterprise_access.apps.subsidy_access_policy.models import (
AssignedLearnerCreditAccessPolicy,
PerLearnerEnrollmentCreditAccessPolicy,
PerLearnerSpendCreditAccessPolicy
)
Expand Down Expand Up @@ -47,3 +48,16 @@ class PerLearnerSpendCapLearnerCreditAccessPolicyFactory(SubsidyAccessPolicyFact

class Meta:
model = PerLearnerSpendCreditAccessPolicy


class AssignedLearnerCreditAccessPolicyFactory(SubsidyAccessPolicyFactory):
"""
Test factory for the `AssignedLearnerCreditAccessPolicy` model.
"""

class Meta:
model = AssignedLearnerCreditAccessPolicy

access_method = AccessMethods.ASSIGNED
per_learner_spend_limit = None
per_learner_enrollment_limit = None
Loading

0 comments on commit 91532f9

Please sign in to comment.