Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions frontends/api/src/generated/v1/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,11 @@ export interface CourseResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof CourseResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -1233,11 +1233,11 @@ export interface LearningPathResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof LearningPathResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -3710,11 +3710,11 @@ export interface PodcastEpisodeResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof PodcastEpisodeResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -3995,11 +3995,11 @@ export interface PodcastResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof PodcastResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -4518,11 +4518,11 @@ export interface ProgramResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof ProgramResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -5210,11 +5210,11 @@ export interface VideoPlaylistResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof VideoPlaylistResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down Expand Up @@ -5489,11 +5489,11 @@ export interface VideoResource {
*/
certification_type: CourseResourceCertificationType
/**
* Returns the prices for the learning resource
* @type {Array<number>}
*
* @type {Array<string>}
* @memberof VideoResource
*/
prices: Array<number>
prices: Array<string>
/**
*
* @type {Array<LearningResourceRun>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const _learningResourceShared = (): Partial<
image: learningResourceImage(),
offered_by: maybe(learningResourceOfferor) ?? null,
platform: maybe(learningResourcePlatform) ?? null,
prices: [0.0],
prices: ["0.00"],
readable_id: faker.lorem.slug(),
course_feature: repeat(faker.lorem.word),
runs: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const getPrice = (resource: LearningResource) => {
return null
}
const price = resource.prices?.[0]
if (resource.platform?.code === PlatformEnum.Ocw || price === 0) {
if (resource.free) {
return "Free"
}
return price ? `$${price}` : null
Expand Down
12 changes: 4 additions & 8 deletions learning_resources/etl/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Q
from django.utils import timezone

from learning_resources.constants import (
LearningResourceFormat,
Expand Down Expand Up @@ -115,12 +113,7 @@ def load_departments(


def load_next_start_date(resource: LearningResource) -> datetime.time | None:
next_upcoming_run = (
resource.runs.filter(Q(published=True) & Q(start_date__gt=timezone.now()))
.order_by("start_date")
.first()
)

next_upcoming_run = resource.next_run
if next_upcoming_run:
resource.next_start_date = next_upcoming_run.start_date
else:
Expand Down Expand Up @@ -227,6 +220,9 @@ def load_run(
image_data = run_data.pop("image", None)
instructors_data = run_data.pop("instructors", [])

# Make sure any prices are unique and sorted in ascending order
run_data["prices"] = sorted(set(run_data.get("prices", [])), key=lambda x: float(x))

with transaction.atomic():
(
learning_resource_run,
Expand Down
12 changes: 7 additions & 5 deletions learning_resources/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ class LearningResourceRunFactory(DjangoModelFactory):
constants.AvailabilityType.archived.value,
)
)
enrollment_start = factory.Faker("date_time", tzinfo=UTC)
enrollment_start = factory.Faker("future_datetime", tzinfo=UTC)
enrollment_end = factory.LazyAttribute(
lambda obj: (
(obj.enrollment_start + timedelta(days=45))
Expand All @@ -490,10 +490,12 @@ class LearningResourceRunFactory(DjangoModelFactory):
end_date = factory.LazyAttribute(
lambda obj: obj.start_date + timedelta(days=90) if obj.start_date else None
)
prices = [
decimal.Decimal(random.uniform(100, 200)) # noqa: S311
for _ in range(random.randint(1, 3)) # noqa: S311
]
prices = sorted(
[
decimal.Decimal(random.uniform(100, 200)) # noqa: S311
for _ in range(random.randint(1, 3)) # noqa: S311
]
)

@factory.post_generation
def instructors(self, create, extracted, **kwargs): # noqa: ARG002
Expand Down
20 changes: 13 additions & 7 deletions learning_resources/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from decimal import Decimal

from django.contrib.admin.utils import flatten
from django.contrib.auth.models import User
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db.models import JSONField
from django.db.models import JSONField, Q
from django.db.models.functions import Lower
from django.utils import timezone

from learning_resources import constants
from learning_resources.constants import (
Expand Down Expand Up @@ -239,18 +239,24 @@ def audience(self) -> str | None:
return self.platform.audience
return None

@property
def next_run(self):
"""Returns the next run for the learning resource"""
return (
self.runs.filter(Q(published=True) & Q(start_date__gt=timezone.now()))
.order_by("start_date")
.first()
)

@property
def prices(self) -> list[Decimal]:
"""Returns the prices for the learning resource"""
if self.resource_type in [
LearningResourceType.course.name,
LearningResourceType.program.name,
]:
return list(
set(
flatten([(run.prices or [Decimal(0.0)]) for run in self.runs.all()])
)
)
next_run = self.next_run
return next_run.prices if next_run else []
else:
return [Decimal(0.00)]

Expand Down
1 change: 1 addition & 0 deletions learning_resources/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ def test_course_creation():
assert resource.topics.count() > 0
assert resource.offered_by is not None
assert resource.runs.count() == course.runs.count()
assert resource.prices == resource.next_run.prices
5 changes: 4 additions & 1 deletion learning_resources/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,10 @@ class LearningResourceBaseSerializer(serializers.ModelSerializer, WriteableTopic
)
certification = serializers.ReadOnlyField(read_only=True)
certification_type = CertificateTypeField(read_only=True)
prices = serializers.ReadOnlyField()
prices = serializers.ListField(
child=serializers.DecimalField(max_digits=10, decimal_places=2),
read_only=True,
)
runs = LearningResourceRunSerializer(read_only=True, many=True, allow_null=True)
image = serializers.SerializerMethodField()
learning_path_parents = serializers.SerializerMethodField()
Expand Down
11 changes: 8 additions & 3 deletions learning_resources/serializers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,22 @@ def test_learning_resource_serializer( # noqa: PLR0913
"platform": serializers.LearningResourcePlatformSerializer(
instance=resource.platform
).data,
"prices": resource.prices,
"prices": sorted([f"{price:.2f}" for price in resource.prices]),
"professional": resource.professional,
"certification": resource.certification,
"certification_type": {
"code": resource.certification_type,
"name": CertificationType[resource.certification_type].value,
},
"free": (
not resource.professional
and detail_key
detail_key
not in (LearningResourceType.course.name, LearningResourceType.program.name)
or (
not resource.professional
and (
not resource.prices or all(price == 0 for price in resource.prices)
)
)
),
"published": resource.published,
"readable_id": resource.readable_id,
Expand Down
42 changes: 21 additions & 21 deletions openapi/specs/v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7223,9 +7223,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -7629,9 +7629,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -9489,9 +9489,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -9734,9 +9734,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -10124,9 +10124,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -10604,9 +10604,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down Expand Up @@ -10839,9 +10839,9 @@ components:
prices:
type: array
items:
type: number
format: double
description: Returns the prices for the learning resource
type: string
format: decimal
pattern: ^-?\d{0,8}(?:\.\d{0,2})?$
readOnly: true
runs:
type: array
Expand Down