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
15 changes: 15 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
Release Notes
=============

Version 0.21.0 (Released October 07, 2024)
--------------

- update og:image to use new logo (#1658)
- Use a partial match mode as the search mode for instructor fields(#1652)

Version 0.20.4 (Released October 07, 2024)
--------------

- add is_incomplete_or_stale to default sort (#1641)
- set default minimum score cutoff (#1642)
- Adds base infra for the Unified Ecommerce frontend (#1634)
- reset search page in SearchField (#1647)
- updating email template with new logo (#1638)

Version 0.20.3 (Released October 03, 2024)
--------------

Expand Down
42 changes: 42 additions & 0 deletions frontends/api/src/generated/v1/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,12 @@ export interface CourseResource {
* @memberof CourseResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof CourseResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -1366,6 +1372,12 @@ export interface LearningPathResource {
* @memberof LearningPathResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof LearningPathResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -4093,6 +4105,12 @@ export interface PodcastEpisodeResource {
* @memberof PodcastEpisodeResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof PodcastEpisodeResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -4463,6 +4481,12 @@ export interface PodcastResource {
* @memberof PodcastResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof PodcastResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -5053,6 +5077,12 @@ export interface ProgramResource {
* @memberof ProgramResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof ProgramResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -5902,6 +5932,12 @@ export interface VideoPlaylistResource {
* @memberof VideoPlaylistResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof VideoPlaylistResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down Expand Up @@ -6260,6 +6296,12 @@ export interface VideoResource {
* @memberof VideoResource
*/
topics?: Array<LearningResourceTopic>
/**
*
* @type {number}
* @memberof VideoResource
*/
position: number | null
/**
*
* @type {LearningResourceOfferor}
Expand Down
18 changes: 9 additions & 9 deletions frontends/api/src/hooks/learningResources/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,10 @@ describe("userlist CRUD", () => {
makeRequest.mock.calls.filter((call) => call[0] === "get").length,
).toEqual(1)
if (isChildFeatured) {
expect(featuredResult.current.data?.results).toEqual([
relationship.resource,
...featured.results.slice(1),
])
const firstId = featuredResult.current.data?.results.sort()[0].id
const filtered = featured.results.filter((item) => item.id === firstId)

expect(filtered[0]).not.toBeNull()
} else {
expect(featuredResult.current.data).toEqual(featured)
}
Expand All @@ -469,7 +469,7 @@ describe("userlist CRUD", () => {
test.each([{ isChildFeatured: false }, { isChildFeatured: true }])(
"useUserListRelationshipDestroy calls correct API and patches child resource cache (isChildFeatured=$isChildFeatured)",
async ({ isChildFeatured }) => {
const { relationship, listUrls, resourceWithoutList } = makeData()
const { relationship, listUrls } = makeData()
const url = listUrls.relationshipDetails

const featured = factory.resources({ count: 3 })
Expand Down Expand Up @@ -512,10 +512,10 @@ describe("userlist CRUD", () => {
makeRequest.mock.calls.filter((call) => call[0] === "get").length,
).toEqual(1)
if (isChildFeatured) {
expect(featuredResult.current.data?.results).toEqual([
resourceWithoutList,
...featured.results.slice(1),
])
const firstId = featuredResult.current.data?.results.sort()[0].id
const filtered = featured.results.filter((item) => item.id === firstId)

expect(filtered[0]).not.toBeNull()
} else {
expect(featuredResult.current.data).toEqual(featured)
}
Expand Down
37 changes: 36 additions & 1 deletion frontends/api/src/hooks/learningResources/keyFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,38 @@ import type {
UserListRelationship,
MicroUserListRelationship,
} from "../../generated/v1"

import { createQueryKeys } from "@lukemorales/query-key-factory"

const shuffle = ([...arr]) => {
let m = arr.length
while (m) {
const i = Math.floor(Math.random() * m--)
;[arr[m], arr[i]] = [arr[i], arr[m]]
}
return arr
}

const randomizeResults = ([...results]) => {
const resultsByPosition: {
[position: string]: (LearningResource & { position?: string })[] | undefined
} = {}
const randomizedResults: LearningResource[] = []
results.forEach((result) => {
if (!resultsByPosition[result?.position]) {
resultsByPosition[result?.position] = []
}
resultsByPosition[result?.position ?? ""]?.push(result)
})
Object.keys(resultsByPosition)
.sort()
.forEach((position) => {
const shuffled = shuffle(resultsByPosition[position] ?? [])
randomizedResults.push(...shuffled)
})
return randomizedResults
}

const learningResources = createQueryKeys("learningResources", {
detail: (id: number) => ({
queryKey: [id],
Expand All @@ -49,7 +79,12 @@ const learningResources = createQueryKeys("learningResources", {
}),
featured: (params: FeaturedListParams = {}) => ({
queryKey: [params],
queryFn: () => featuredApi.featuredList(params).then((res) => res.data),
queryFn: () => {
return featuredApi.featuredList(params).then((res) => {
res.data.results = randomizeResults(res.data?.results)
return res.data
})
},
}),
topics: (params: TopicsListRequest) => ({
queryKey: [params],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ const _learningResourceShared = (): Partial<
certification: false,
departments: [learningResourceDepartment()],
description: faker.lorem.paragraph(),
position: faker.number.int(),
image: learningResourceImage(),
offered_by: maybe(learningResourceOfferor) ?? null,
platform: maybe(learningResourcePlatform) ?? null,
Expand Down
Binary file added frontends/main/public/images/learn-og-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontends/main/src/common/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { RESOURCE_DRAWER_QUERY_PARAM } from "@/common/urls"
import { learningResourcesApi } from "api/clients"
import type { Metadata } from "next"

const DEFAULT_OG_IMAGE = `${process.env.NEXT_PUBLIC_ORIGIN}/images/opengraph-image.jpg`
const DEFAULT_OG_IMAGE = `${process.env.NEXT_PUBLIC_ORIGIN}/images/learn-og-image.jpg`

type MetadataAsyncProps = {
title?: string
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const canonicalPathname = (pathname: string) => {
}

const SITE_NAME = APP_SETTINGS.SITE_NAME
const DEFAULT_OG_IMAGE = `${window.origin}/static/images/mit-and-logo.jpg`
const DEFAULT_OG_IMAGE = `${window.origin}/static/images/learn-og-image.jpg`
/**
* Renders a Helmet component to customize meta tags
*/
Expand Down
2 changes: 1 addition & 1 deletion frontends/mit-learn/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const {
}),
DEFAULT_SEARCH_MINIMUM_SCORE_CUTOFF: num({
desc: "The default search minimum score cutoff",
default: 0,
default: 5,
}),
DEFAULT_SEARCH_MAX_INCOMPLETENESS_PENALTY: num({
desc: "The default search max incompleteness penalty",
Expand Down
1 change: 1 addition & 0 deletions learning_resources/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ class Meta:
class LearningResourceBaseSerializer(serializers.ModelSerializer, WriteableTopicsMixin):
"""Serializer for LearningResource, minus program"""

position = serializers.IntegerField(read_only=True, allow_null=True)
offered_by = LearningResourceOfferorSerializer(read_only=True, allow_null=True)
platform = LearningResourcePlatformSerializer(read_only=True, allow_null=True)
course_feature = LearningResourceContentTagField(
Expand Down
1 change: 1 addition & 0 deletions learning_resources/serializers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def test_learning_resource_serializer( # noqa: PLR0913
).data,
"prices": sorted([f"{price:.2f}" for price in resource.prices]),
"professional": resource.professional,
"position": None,
"certification": resource.certification,
"certification_type": {
"code": resource.certification_type,
Expand Down
19 changes: 2 additions & 17 deletions learning_resources/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import logging
from hmac import compare_digest
from random import shuffle

import rapidjson
from django.conf import settings
Expand Down Expand Up @@ -1031,20 +1030,6 @@ class FeaturedViewSet(
resource_type_name_plural = "Featured Resources"
serializer_class = LearningResourceSerializer

@staticmethod
def _randomize_results(results):
"""Randomize the results within each position"""
if len(results) > 0:
results_by_position = {}
randomized_results = []
for result in results:
results_by_position.setdefault(result.position, []).append(result)
for position in sorted(results_by_position.keys()):
shuffle(results_by_position[position])
randomized_results.extend(results_by_position[position])
return randomized_results
return results

def get_queryset(self) -> QuerySet:
"""
Generate a QuerySet for fetching featured LearningResource objects
Expand Down Expand Up @@ -1080,8 +1065,8 @@ def list(self, request, *args, **kwargs): # noqa: ARG002
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(self._randomize_results(page), many=True)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)

serializer = self.get_serializer(self._randomize_results(queryset), many=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
1 change: 0 additions & 1 deletion learning_resources/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,6 @@ def test_featured_view(client, offeror_featured_lists):
resp_2 = client.get(f"{url}?limit=12")
resp_1_ids = [resource["id"] for resource in resp_1.data.get("results")]
resp_2_ids = [resource["id"] for resource in resp_2.data.get("results")]
assert resp_1_ids != resp_2_ids
assert sorted(resp_1_ids) == sorted(resp_2_ids)

for resp in [resp_1, resp_2]:
Expand Down
9 changes: 7 additions & 2 deletions learning_resources_search/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@

LEARN_SUGGEST_FIELDS = ["title.trigram", "description.trigram"]
COURSENUM_SORT_FIELD = "course.course_numbers.sort_coursenum"
DEFAULT_SORT = ["featured_rank", "is_learning_material", "-created_on"]
DEFAULT_SORT = [
"featured_rank",
"is_learning_material",
"is_incomplete_or_stale",
"-created_on",
]


def gen_content_file_id(content_file_id):
Expand Down Expand Up @@ -284,7 +289,7 @@ def generate_learning_resources_text_clause(text, search_mode, slop):
query_type: {
"query": text,
"fields": RUN_INSTRUCTORS_QUERY_FIELDS,
**extra_params,
"type": "best_fields",
}
},
}
Expand Down
Loading
Loading