diff --git a/frontends/mit-open/src/pages/FieldPage/FieldPage.test.tsx b/frontends/mit-open/src/pages/FieldPage/FieldPage.test.tsx
index de419fb1d8..feda034f4f 100644
--- a/frontends/mit-open/src/pages/FieldPage/FieldPage.test.tsx
+++ b/frontends/mit-open/src/pages/FieldPage/FieldPage.test.tsx
@@ -191,10 +191,10 @@ describe("FieldPage", () => {
const { field } = setupApis({ search_filter: "q=ocw" }, {}, true, true)
renderTestApp({ url: `/c/${field.channel_type}/${field.name}` })
await screen.findByText(field.title)
- const subscribedButton = await screen.findByText("Subscribed")
+ const subscribedButton = await screen.findByText("Follow")
assertInstanceOf(subscribedButton, HTMLButtonElement)
user.click(subscribedButton)
- const unsubscribeButton = await screen.findByText("Unsubscribe")
+ const unsubscribeButton = await screen.findByText("Unfollow")
assertInstanceOf(unsubscribeButton, HTMLLIElement)
})
@@ -202,7 +202,7 @@ describe("FieldPage", () => {
const { field } = setupApis({ search_filter: "q=ocw" }, {}, true, false)
renderTestApp({ url: `/c/${field.channel_type}/${field.name}` })
await screen.findByText(field.title)
- const subscribeButton = await screen.findByText("Subscribe")
+ const subscribeButton = await screen.findByText("Follow")
assertInstanceOf(subscribeButton, HTMLButtonElement)
})
it("Hides the subscribe toggle if the user is not authenticated", async () => {
@@ -210,7 +210,7 @@ describe("FieldPage", () => {
renderTestApp({ url: `/c/${field.channel_type}/${field.name}` })
await screen.findByText(field.title)
await waitFor(() => {
- expect(screen.queryByText("Subscribe")).not.toBeInTheDocument()
+ expect(screen.queryByText("Follow")).not.toBeInTheDocument()
})
})
})
diff --git a/frontends/mit-open/src/pages/FieldPage/FieldPageSkeleton.tsx b/frontends/mit-open/src/pages/FieldPage/FieldPageSkeleton.tsx
index 4e08e79213..a113d85d33 100644
--- a/frontends/mit-open/src/pages/FieldPage/FieldPageSkeleton.tsx
+++ b/frontends/mit-open/src/pages/FieldPage/FieldPageSkeleton.tsx
@@ -9,6 +9,7 @@ import {
Box,
Breadcrumbs,
} from "ol-components"
+import { MetaTags } from "ol-utilities"
import { SearchSubscriptionToggle } from "@/page-components/SearchSubscriptionToggle/SearchSubscriptionToggle"
import { ChannelDetails } from "@/page-components/ChannelDetails/ChannelDetails"
import { useChannelDetail } from "api/hooks/fields"
@@ -101,89 +102,110 @@ const FieldSkeletonProps: React.FC
= ({
]
return (
-
-
-
- {field.data && (
-
+ <>
+
+
+
+
+ {field.data && (
({
+ flexDirection="column"
+ alignItems="start"
+ sx={{
flexGrow: 1,
flexShrink: 0,
order: 1,
- py: "24px",
-
- [theme.breakpoints.down("md")]: {
- py: 0,
- pb: "8px",
- },
- })}
+ width: "50%",
+ }}
>
- {displayConfiguration?.logo ? (
-
+ ({
+ flexGrow: 1,
+ flexShrink: 0,
+ order: 1,
+ py: "24px",
+
+ [theme.breakpoints.down("md")]: {
+ py: 0,
+ pb: "8px",
+ },
+ })}
+ >
+ {displayConfiguration?.logo ? (
+
+ ) : (
+
+
+ {field.data.title}
+
+
+ )}
+
+ {displayConfiguration.heading ? (
+
+
+ {displayConfiguration.heading}
+
+
) : (
-
-
- {field.data.title}
-
-
+ <>>
)}
-
- {displayConfiguration.heading ? (
= ({
my: 1,
}}
>
-
- {displayConfiguration.heading}
+
+ {displayConfiguration.sub_heading}
- ) : (
- <>>
- )}
-
-
- {displayConfiguration.sub_heading}
-
+ {channelType === "unit" ? (
+
+
+ {field.data?.search_filter ? (
+
+ ) : null}
+ {field.data?.is_moderator ? (
+
+ ) : null}
+
+
+ ) : null}
{channelType === "unit" ? (
+
+
+
+ ) : (
@@ -248,69 +299,24 @@ const FieldSkeletonProps: React.FC = ({
) : null}
- ) : null}
+ )}
- {channelType === "unit" ? (
-
-
-
- ) : (
-
-
- {field.data?.search_filter ? (
-
- ) : null}
- {field.data?.is_moderator ? (
-
- ) : null}
-
-
- )}
-
- )}
-
-
- }
- >
- {channelType === "unit" ? (
-
-
-
- ) : null}
- {children}
-
+ )}
+
+
+ }
+ >
+ {channelType === "unit" ? (
+
+
+
+ ) : null}
+ {children}
+
+ >
)
}
diff --git a/frontends/mit-open/src/pages/HomePage/HomePage.tsx b/frontends/mit-open/src/pages/HomePage/HomePage.tsx
index 8872ff9c03..bf90af1250 100644
--- a/frontends/mit-open/src/pages/HomePage/HomePage.tsx
+++ b/frontends/mit-open/src/pages/HomePage/HomePage.tsx
@@ -1,5 +1,6 @@
import React from "react"
import { Container, styled } from "ol-components"
+import { MetaTags } from "ol-utilities"
import HeroSearch from "./HeroSearch"
import BrowseTopicsSection from "./BrowseTopicsSection"
import NewsEventsSection from "./NewsEventsSection"
@@ -35,6 +36,7 @@ const MediaCarousel = styled(ResourceCarousel)(({ theme }) => ({
const HomePage: React.FC = () => {
return (
<>
+
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
index 4afee0591f..75fac9d5b7 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
@@ -37,7 +37,9 @@ describe("LearningPathListingPage", () => {
it("Has title 'Learning Paths'", async () => {
setup()
screen.getByRole("heading", { name: "Learning Paths" })
- await waitFor(() => expect(document.title).toBe("Learning Paths"))
+ await waitFor(() =>
+ expect(document.title).toBe("Learning Paths | MIT Open"),
+ )
})
it("Renders a card for each learning path", async () => {
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
index 604c428f58..5802a15c5e 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
@@ -93,9 +93,7 @@ const LearningPathListingPage: React.FC = () => {
src="/static/images/course_search_banner.png"
className="learningpaths-page"
>
-
- Learning Paths
-
+
diff --git a/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.test.ts b/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.test.ts
index b5d7082a69..1b7ba69ac3 100644
--- a/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.test.ts
+++ b/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.test.ts
@@ -71,7 +71,7 @@ describe("ListDetailsPage", () => {
const path = factories.learningResources.learningPath()
setup({ path })
await screen.findByRole("heading", { name: path.title })
- await waitFor(() => expect(document.title).toBe(path.title))
+ await waitFor(() => expect(document.title).toBe(`${path.title} | MIT Open`))
})
test.each([
diff --git a/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.tsx b/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.tsx
index dd52015a22..4c78a70a6c 100644
--- a/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.tsx
+++ b/frontends/mit-open/src/pages/ListDetailsPage/ListDetailsPage.tsx
@@ -114,9 +114,7 @@ const ListDetailsPage: React.FC = ({
src="/static/images/course_search_banner.png"
className="learningpaths-page"
>
-
- {title}
-
+
{
return activeStep < NUM_STEPS ? (
-
- Onboarding
-
+
diff --git a/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.test.tsx b/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.test.tsx
index 201ec15e7c..c354c1dd61 100644
--- a/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.test.tsx
+++ b/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.test.tsx
@@ -18,7 +18,7 @@ describe("PrivacyPage", () => {
url: commonUrls.PRIVACY,
})
await waitFor(() => {
- expect(document.title).toBe("Privacy Policy")
+ expect(document.title).toBe("Privacy Policy | MIT Open")
})
screen.getByRole("heading", {
name: "Privacy Policy",
diff --git a/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.tsx b/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.tsx
index 77a025c459..dc4ea0b08a 100644
--- a/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.tsx
+++ b/frontends/mit-open/src/pages/PrivacyPage/PrivacyPage.tsx
@@ -55,9 +55,7 @@ const PrivacyPage: React.FC = () => {
return (
-
- Privacy Policy
-
+
{
return (
<>
-
- Search
-
+
diff --git a/frontends/mit-open/src/pages/TermsPage/TermsPage.test.tsx b/frontends/mit-open/src/pages/TermsPage/TermsPage.test.tsx
index e2fa01ea5d..21d37d92da 100644
--- a/frontends/mit-open/src/pages/TermsPage/TermsPage.test.tsx
+++ b/frontends/mit-open/src/pages/TermsPage/TermsPage.test.tsx
@@ -18,7 +18,7 @@ describe("TermsPage", () => {
url: commonUrls.TERMS,
})
await waitFor(() => {
- expect(document.title).toBe("Terms of Service")
+ expect(document.title).toBe("Terms of Service | MIT Open")
})
screen.getByRole("heading", {
name: "Terms of Service",
diff --git a/frontends/mit-open/src/pages/TermsPage/TermsPage.tsx b/frontends/mit-open/src/pages/TermsPage/TermsPage.tsx
index 316d29bb06..635844e448 100644
--- a/frontends/mit-open/src/pages/TermsPage/TermsPage.tsx
+++ b/frontends/mit-open/src/pages/TermsPage/TermsPage.tsx
@@ -59,9 +59,7 @@ const TermsPage: React.FC = () => {
return (
-
- Terms of Service
-
+
{
setupApis()
renderWithProviders()
await waitFor(() => {
- expect(document.title).toBe("MIT Open | Topics")
+ expect(document.title).toBe("Topics | MIT Open")
})
screen.getByRole("heading", { name: "Topics" })
})
diff --git a/frontends/mit-open/src/pages/TopicListingPage/TopicsListingPage.tsx b/frontends/mit-open/src/pages/TopicListingPage/TopicsListingPage.tsx
index 705c307d1d..cf3644a0bd 100644
--- a/frontends/mit-open/src/pages/TopicListingPage/TopicsListingPage.tsx
+++ b/frontends/mit-open/src/pages/TopicListingPage/TopicsListingPage.tsx
@@ -278,9 +278,7 @@ const ToopicsListingPage: React.FC = () => {
}, [topicsQuery.data?.results, courseQuery.data, programQuery.data])
return (
-
- MIT Open | Topics
-
+
{
setupApis()
renderWithProviders()
await waitFor(() => {
- expect(document.title).toBe("MIT Open | Units")
+ expect(document.title).toBe("Units | MIT Open")
})
screen.getByRole("heading", { name: "Academic & Professional Learning" })
})
diff --git a/frontends/mit-open/src/pages/UnitsListingPage/UnitsListingPage.tsx b/frontends/mit-open/src/pages/UnitsListingPage/UnitsListingPage.tsx
index e27489140a..2506991e80 100644
--- a/frontends/mit-open/src/pages/UnitsListingPage/UnitsListingPage.tsx
+++ b/frontends/mit-open/src/pages/UnitsListingPage/UnitsListingPage.tsx
@@ -405,9 +405,7 @@ const UnitsListingPage: React.FC = () => {
return (
-
- MIT Open | Units
-
+
{
- it("Has title 'User Lists'", async () => {
+ it("Has heading 'User Lists' and correct page title", async () => {
setup()
screen.getByRole("heading", { name: "User Lists" })
- await waitFor(() => expect(document.title).toBe("User Lists"))
+ await waitFor(() => expect(document.title).toBe("My Lists | MIT Open"))
})
it("Renders a card for each user list", async () => {
diff --git a/frontends/mit-open/src/pages/UserListListingPage/UserListListingPage.tsx b/frontends/mit-open/src/pages/UserListListingPage/UserListListingPage.tsx
index 249bd03057..4f783cf40f 100644
--- a/frontends/mit-open/src/pages/UserListListingPage/UserListListingPage.tsx
+++ b/frontends/mit-open/src/pages/UserListListingPage/UserListListingPage.tsx
@@ -164,9 +164,7 @@ const UserListListingPage: React.FC = () => {
src="/static/images/course_search_banner.png"
className="learningpaths-page"
>
-
- User Lists
-
+
{
// within app, define process.env.VAR_NAME with default from cleanEnv
MITOPEN_AXIOS_BASE_PATH,
ENVIRONMENT,
+ SITE_NAME,
}),
]
.concat(
diff --git a/frontends/ol-components/src/components/NavDrawer/NavDrawer.tsx b/frontends/ol-components/src/components/NavDrawer/NavDrawer.tsx
index 2a8564aa86..70d1f89d82 100644
--- a/frontends/ol-components/src/components/NavDrawer/NavDrawer.tsx
+++ b/frontends/ol-components/src/components/NavDrawer/NavDrawer.tsx
@@ -182,6 +182,7 @@ const NavDrawer = (props: NavDrawerProps) => {
hideBackdrop={true}
PaperProps={{
sx: {
+ borderRight: "none",
boxShadow: "0px 6px 24px 0px rgba(37, 38, 43, 0.10)",
zIndex: (theme) => theme.zIndex.appBar - 1,
overscrollBehavior: "contain",
diff --git a/frontends/ol-utilities/src/components/MetaTags.tsx b/frontends/ol-utilities/src/components/MetaTags.tsx
index 7c94129e86..6564a0a933 100644
--- a/frontends/ol-utilities/src/components/MetaTags.tsx
+++ b/frontends/ol-utilities/src/components/MetaTags.tsx
@@ -2,6 +2,7 @@ import React from "react"
import { Helmet } from "react-helmet-async"
type MetaTagsProps = {
+ title?: string | string[]
canonicalLink?: string
children?: React.ReactNode
}
@@ -17,14 +18,22 @@ const getCanonicalUrl = (url: string): string => {
/**
* Renders a Helmet component to customize meta tags
*/
-const MetaTags: React.FC = ({ children, canonicalLink }) =>
- children || canonicalLink ? (
+const MetaTags: React.FC = ({
+ title,
+ children,
+ canonicalLink,
+}) => {
+ title = title ? (Array.isArray(title) ? title : [title]) : []
+
+ return (
+ {[...title, process.env.SITE_NAME].join(" | ")}
{children}
{canonicalLink ? (
) : null}
- ) : null
+ )
+}
export default MetaTags
diff --git a/learning_resources/etl/loaders.py b/learning_resources/etl/loaders.py
index 61a71c20c9..b2076c60a5 100644
--- a/learning_resources/etl/loaders.py
+++ b/learning_resources/etl/loaders.py
@@ -512,14 +512,24 @@ def load_programs(
blocklist = load_course_blocklist()
duplicates = load_course_duplicates(etl_source)
- return [
- program
- for program in [
- load_program(program_data, blocklist, duplicates, config=config)
- for program_data in programs_data
- ]
- if program is not None
+ programs = [
+ load_program(program_data, blocklist, duplicates, config=config)
+ for program_data in programs_data
]
+ if programs and config.prune:
+ for learning_resource in LearningResource.objects.filter(
+ etl_source=etl_source, resource_type=LearningResourceType.program.name
+ ).exclude(
+ id__in=[
+ learning_resource.id
+ for learning_resource in programs
+ if learning_resource is not None
+ ]
+ ):
+ learning_resource.published = False
+ learning_resource.save()
+ resource_unpublished_actions(learning_resource)
+ return [program for program in programs if program is not None]
def load_content_file(
diff --git a/learning_resources/etl/loaders_test.py b/learning_resources/etl/loaders_test.py
index e4472ef2ec..d33cd3a9bc 100644
--- a/learning_resources/etl/loaders_test.py
+++ b/learning_resources/etl/loaders_test.py
@@ -743,11 +743,14 @@ def test_load_courses(mocker, mock_blocklist, mock_duplicates, prune):
def test_load_programs(mocker, mock_blocklist, mock_duplicates):
"""Test that load_programs calls the expected functions"""
- program_data = [{"courses": [{"platform": "a"}, {}]}]
+ program_data = [{"courses": [{"platform": "a"}, {}], "id": 5}]
+
mock_load_program = mocker.patch(
- "learning_resources.etl.loaders.load_program", autospec=True
+ "learning_resources.etl.loaders.load_program",
+ autospec=True,
+ return_value=ProgramFactory.create().learning_resource,
)
- load_programs("mitx", program_data)
+ load_programs("mitx", program_data, config=ProgramLoaderConfig(prune=True))
assert mock_load_program.call_count == len(program_data)
mock_blocklist.assert_called_once()
mock_duplicates.assert_called_once_with("mitx")
diff --git a/learning_resources/etl/mitxonline.py b/learning_resources/etl/mitxonline.py
index 0a1a6ca697..e25ec6a283 100644
--- a/learning_resources/etl/mitxonline.py
+++ b/learning_resources/etl/mitxonline.py
@@ -90,7 +90,15 @@ def parse_page_attribute(
def extract_programs():
"""Loads the MITx Online catalog data""" # noqa: D401
if settings.MITX_ONLINE_PROGRAMS_API_URL:
- return list(_fetch_data(settings.MITX_ONLINE_PROGRAMS_API_URL))
+ return list(
+ _fetch_data(
+ settings.MITX_ONLINE_PROGRAMS_API_URL,
+ params={
+ "page__live": True,
+ "live": True,
+ },
+ )
+ )
else:
log.warning("Missing required setting MITX_ONLINE_PROGRAMS_API_URL")
@@ -100,7 +108,15 @@ def extract_programs():
def extract_courses():
"""Loads the MITx Online catalog data""" # noqa: D401
if settings.MITX_ONLINE_COURSES_API_URL:
- return list(_fetch_data(settings.MITX_ONLINE_COURSES_API_URL))
+ return list(
+ _fetch_data(
+ settings.MITX_ONLINE_COURSES_API_URL,
+ params={
+ "page__live": True,
+ "live": True,
+ },
+ )
+ )
else:
log.warning("Missing required setting MITX_ONLINE_COURSES_API_URL")
@@ -210,6 +226,8 @@ def _transform_course(course):
},
"published": bool(
parse_page_attribute(course, "page_url")
+ and parse_page_attribute(course, "live")
+ and course.get("live", False)
), # a course is only considered published if it has a page url
"professional": False,
"certification": has_certification,
@@ -244,7 +262,11 @@ def _fetch_courses_by_ids(course_ids):
return list(
_fetch_data(
settings.MITX_ONLINE_COURSES_API_URL,
- params={"id": ",".join([str(courseid) for courseid in course_ids])},
+ params={
+ "id": ",".join([str(courseid) for courseid in course_ids]),
+ "page__live": True,
+ "live": True,
+ },
)
)
diff --git a/learning_resources/etl/mitxonline_test.py b/learning_resources/etl/mitxonline_test.py
index 333bfa582b..d1fc49f321 100644
--- a/learning_resources/etl/mitxonline_test.py
+++ b/learning_resources/etl/mitxonline_test.py
@@ -194,6 +194,8 @@ def test_mitxonline_transform_programs(
),
"published": bool(
course_data.get("page", {}).get("page_url", None)
+ and course_data.get("page", {}).get("live", None)
+ and course_data.get("live", None)
),
"certification": True,
"certification_type": CertificationType.completion.name,
@@ -303,7 +305,11 @@ def test_mitxonline_transform_courses(settings, mock_mitxonline_courses_data):
course_data.get("page", {}).get("description", None)
),
"offered_by": OFFERED_BY,
- "published": course_data.get("page", {}).get("page_url", None) is not None,
+ "published": bool(
+ course_data.get("page", {}).get("page_url", None)
+ and course_data.get("page", {}).get("live", None)
+ and course_data.get("live", None)
+ ),
"professional": False,
"certification": parse_certification(
"mitx",
diff --git a/learning_resources/etl/pipelines.py b/learning_resources/etl/pipelines.py
index 90db29f0f6..7ccf48d062 100644
--- a/learning_resources/etl/pipelines.py
+++ b/learning_resources/etl/pipelines.py
@@ -53,7 +53,7 @@
mitxonline_programs_etl = compose(
load_programs(
ETLSource.mitxonline.name,
- config=ProgramLoaderConfig(courses=CourseLoaderConfig(prune=True)),
+ config=ProgramLoaderConfig(courses=CourseLoaderConfig(prune=True), prune=True),
),
mitxonline.transform_programs,
mitxonline.extract_programs,
diff --git a/learning_resources/etl/pipelines_test.py b/learning_resources/etl/pipelines_test.py
index fb1525cca3..af94bda19d 100644
--- a/learning_resources/etl/pipelines_test.py
+++ b/learning_resources/etl/pipelines_test.py
@@ -79,7 +79,7 @@ def test_mitxonline_programs_etl():
mock_load_programs.assert_called_once_with(
ETLSource.mitxonline.name,
mock_transform.return_value,
- config=ProgramLoaderConfig(courses=CourseLoaderConfig(prune=True)),
+ config=ProgramLoaderConfig(courses=CourseLoaderConfig(prune=True), prune=True),
)
assert result == mock_load_programs.return_value
diff --git a/learning_resources/management/commands/populate_featured_lists.py b/learning_resources/management/commands/populate_featured_lists.py
index fe3c50cb32..cf106f3913 100644
--- a/learning_resources/management/commands/populate_featured_lists.py
+++ b/learning_resources/management/commands/populate_featured_lists.py
@@ -48,7 +48,7 @@ def handle(self, *args, **options): # noqa: ARG002
# Get the channel for the offeror
unit_channel = FieldChannel.objects.filter(
- unit_detail__offeror=offeror
+ unit_detail__unit=offeror
).first()
if not unit_channel:
self.stderr.write(
@@ -89,6 +89,6 @@ def handle(self, *args, **options): # noqa: ARG002
total_seconds = (now_in_utc() - start).total_seconds()
self.stdout.write(
- "Population of offeror channel featured lists finished, "
+ "Population of unit channel featured lists finished, "
f"took {total_seconds} seconds"
)
diff --git a/learning_resources/models.py b/learning_resources/models.py
index 72bbdb4780..0f0769dbaa 100644
--- a/learning_resources/models.py
+++ b/learning_resources/models.py
@@ -255,8 +255,11 @@ def prices(self) -> list[Decimal]:
LearningResourceType.course.name,
LearningResourceType.program.name,
]:
- next_run = self.next_run
- return next_run.prices if next_run else []
+ next_run = (
+ self.next_run
+ or self.runs.filter(published=True).order_by("-start_date").first()
+ )
+ return next_run.prices if next_run and next_run.prices else []
else:
return [Decimal(0.00)]
diff --git a/learning_resources/models_test.py b/learning_resources/models_test.py
index 4f0db4d45c..823105e1a4 100644
--- a/learning_resources/models_test.py
+++ b/learning_resources/models_test.py
@@ -1,10 +1,15 @@
"""Tests for learning_resources.models"""
+from datetime import timedelta
+from decimal import Decimal
+
import pytest
+from django.db.models import F
from learning_resources.constants import LearningResourceType
from learning_resources.factories import (
CourseFactory,
+ LearningResourceRunFactory,
ProgramFactory,
)
@@ -48,3 +53,30 @@ def test_course_creation():
assert resource.offered_by is not None
assert resource.runs.count() == course.runs.count()
assert resource.prices == resource.next_run.prices
+
+
+def test_course_prices_current_no_next():
+ """Test that course.prices == most recent run prices if no next run"""
+ course = CourseFactory.create()
+ resource = course.learning_resource
+ resource.runs.update(start_date=F("start_date") - timedelta(days=3650))
+ unpub_run = LearningResourceRunFactory.create(
+ learning_resource=resource, published=False, prices=[Decimal("987654.32")]
+ )
+ resource.refresh_from_db()
+ most_recent_run = resource.runs.filter(published=True).order_by("start_date").last()
+ assert most_recent_run != unpub_run
+ assert resource.next_run is None
+ # Prices should be from most recent published run if no next run
+ assert resource.prices == most_recent_run.prices
+ assert len(resource.prices) > 0
+
+
+def test_course_prices_unpublished_runs():
+ """Test that course.prices == [] if no published run"""
+ course = CourseFactory.create()
+ resource = course.learning_resource
+ resource.runs.update(published=False)
+ resource.refresh_from_db()
+ assert resource.next_run is None
+ assert resource.prices == []
diff --git a/learning_resources_search/api.py b/learning_resources_search/api.py
index 2f7fe833ce..4b6c20af70 100644
--- a/learning_resources_search/api.py
+++ b/learning_resources_search/api.py
@@ -38,7 +38,7 @@
LEARN_SUGGEST_FIELDS = ["title.trigram", "description.trigram"]
COURSENUM_SORT_FIELD = "course.course_numbers.sort_coursenum"
-DEFAULT_SORT = ["is_learning_material", "-created_on"]
+DEFAULT_SORT = ["is_learning_material", "-views"]
def gen_content_file_id(content_file_id):
diff --git a/learning_resources_search/api_test.py b/learning_resources_search/api_test.py
index 144b4b721d..ecc3bf6051 100644
--- a/learning_resources_search/api_test.py
+++ b/learning_resources_search/api_test.py
@@ -1762,7 +1762,7 @@ def test_document_percolation(opensearch, mocker):
[
("-views", None, [{"views": {"order": "desc"}}]),
("-views", "text", [{"views": {"order": "desc"}}]),
- (None, None, ["is_learning_material", {"created_on": {"order": "desc"}}]),
+ (None, None, ["is_learning_material", {"views": {"order": "desc"}}]),
(None, "text", None),
],
)
diff --git a/main/settings.py b/main/settings.py
index ffa3574854..a48c9eb9cc 100644
--- a/main/settings.py
+++ b/main/settings.py
@@ -33,7 +33,7 @@
from main.settings_pluggy import * # noqa: F403
from openapi.settings_spectacular import open_spectacular_settings
-VERSION = "0.13.8"
+VERSION = "0.13.9"
log = logging.getLogger()