Skip to content

Commit

Permalink
👔(backend) favor author organization at organization order assignment
Browse files Browse the repository at this point in the history
In case of order count equality, we would like to favor organization which are
also author of the course which means it is also linked directly to the related
course.
  • Loading branch information
jbpenrath committed Sep 19, 2024
1 parent 35893fc commit ce938db
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to

### Changed

- Update round robin logic to favor author organizations
- Reassign organization for pending orders

### Fixed
Expand Down
58 changes: 34 additions & 24 deletions src/backend/joanie/core/api/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@
from django.core.exceptions import ValidationError
from django.core.files.storage import storages
from django.db import IntegrityError, transaction
from django.db.models import Count, OuterRef, Prefetch, Q, Subquery
from django.db.models import (
BooleanField,
Case,
Count,
ExpressionWrapper,
OuterRef,
Prefetch,
Q,
Subquery,
Value,
When,
)
from django.http import FileResponse, Http404, HttpResponse, JsonResponse
from django.urls import reverse
from django.utils import timezone
Expand Down Expand Up @@ -323,36 +334,35 @@ def _get_organization_with_least_active_orders(
Return the organization with the least not canceled order count
for a given product and course.
"""
if enrollment:
clause = Q(order__enrollment=enrollment)
else:
clause = Q(order__course=course)

order_count = Count(
"order",
filter=clause
& Q(order__product=product)
& ~Q(
order__state__in=[
enums.ORDER_STATE_CANCELED,
enums.ORDER_STATE_PENDING,
]
),
)
course_id = course.id if course else enrollment.course_run.course_id

try:
course_relation = product.course_relations.get(
course_id=course.id if course else enrollment.course_run.course_id
)
course_relation = product.course_relations.get(course_id=course_id)
except models.CourseProductRelation.DoesNotExist:
return None

order_count_filter = Q(order__product=product) & ~Q(
order__state__in=[
enums.ORDER_STATE_CANCELED,
enums.ORDER_STATE_PENDING,
]
)
if enrollment:
order_count_filter &= Q(order__enrollment=enrollment)
else:
order_count_filter &= Q(order__course=course)

try:
return (
course_relation.organizations.annotate(order_count=order_count)
.order_by("order_count")
.first()
organizations = course_relation.organizations.annotate(
order_count=Count("order", filter=order_count_filter),
is_author=Case(
When(Q(courses__id=course_id), then=Value(True)),
default=Value(False),
output_field=BooleanField(),
),
)

return organizations.order_by("order_count", "-is_author", "?").first()
except models.Organization.DoesNotExist:
return None

Expand Down
67 changes: 67 additions & 0 deletions src/backend/joanie/tests/core/api/order/test_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,70 @@ def test_api_order_submit_auto_assign_organization_with_least_orders(self):

order.refresh_from_db()
self.assertEqual(order.organization, expected_organization)

def test_api_order_submit_get_organization_with_least_active_orders_prefer_author(
self,
):
"""
In case of order count equality, the method _get_organization_with_least_orders should
return first organization which is also an author of the course.
"""
user = factories.UserFactory()
token = self.generate_token_from_user(user)

organization, expected_organization = (
factories.OrganizationFactory.create_batch(2)
)

relation = factories.CourseProductRelationFactory(
organizations=[organization, expected_organization]
)

relation.course.organizations.set([expected_organization])

# Create 3 orders for the first organization (1 draft, 1 pending, 1 canceled)
factories.OrderFactory(
organization=organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_PENDING,
)
factories.OrderFactory(
organization=organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_CANCELED,
)

# 2 ignored orders for the second organization (1 pending, 1 canceled)
factories.OrderFactory(
organization=expected_organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_PENDING,
)
factories.OrderFactory(
organization=expected_organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_CANCELED,
)

# Then create an order without organization
order = factories.OrderFactory(
owner=user,
product=relation.product,
course=relation.course,
organization=None,
)

self.client.patch(
f"/api/v1.0/orders/{order.id}/submit/",
content_type="application/json",
data={"billing_address": BillingAddressDictFactory()},
HTTP_AUTHORIZATION=f"Bearer {token}",
)

order.refresh_from_db()

self.assertEqual(order.organization, expected_organization)

0 comments on commit ce938db

Please sign in to comment.