diff --git a/RELEASE.rst b/RELEASE.rst index 1ae708ba43..a899b8533d 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,12 @@ Release Notes ============= +Version 0.19.4 (Released September 25, 2024) +-------------- + +- new -> recently added (#1594) +- Pace and format fields for learning resources (#1588) + Version 0.19.3 (Released September 23, 2024) -------------- diff --git a/frontends/api/src/generated/v1/api.ts b/frontends/api/src/generated/v1/api.ts index ff24f6357f..cf9b817ed5 100644 --- a/frontends/api/src/generated/v1/api.ts +++ b/frontends/api/src/generated/v1/api.ts @@ -758,6 +758,12 @@ export interface CourseResource { * @memberof CourseResource */ url?: string | null + /** + * + * @type {Array} + * @memberof CourseResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -972,6 +978,12 @@ export interface CourseResourceRequest { * @memberof CourseResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof CourseResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -1516,6 +1528,12 @@ export interface LearningPathResource { * @memberof LearningPathResource */ url?: string | null + /** + * + * @type {Array} + * @memberof LearningPathResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -1608,6 +1626,12 @@ export interface LearningPathResourceRequest { * @memberof LearningPathResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof LearningPathResourceRequest + */ + ocw_topics?: Array /** * * @type {boolean} @@ -3439,6 +3463,12 @@ export interface PatchedLearningPathResourceRequest { * @memberof PatchedLearningPathResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof PatchedLearningPathResourceRequest + */ + ocw_topics?: Array /** * * @type {boolean} @@ -3652,6 +3682,12 @@ export interface PercolateQuerySubscriptionRequestRequest { * @memberof PercolateQuerySubscriptionRequestRequest */ topic?: Array + /** + * The ocw topic name. + * @type {Array} + * @memberof PercolateQuerySubscriptionRequestRequest + */ + ocw_topic?: Array /** * If true return raw open search results with score explanations * @type {boolean} @@ -4213,6 +4249,12 @@ export interface PodcastEpisodeResource { * @memberof PodcastEpisodeResource */ url?: string | null + /** + * + * @type {Array} + * @memberof PodcastEpisodeResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -4311,6 +4353,12 @@ export interface PodcastEpisodeResourceRequest { * @memberof PodcastEpisodeResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof PodcastEpisodeResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -4571,6 +4619,12 @@ export interface PodcastResource { * @memberof PodcastResource */ url?: string | null + /** + * + * @type {Array} + * @memberof PodcastResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -4669,6 +4723,12 @@ export interface PodcastResourceRequest { * @memberof PodcastResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof PodcastResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -5149,6 +5209,12 @@ export interface ProgramResource { * @memberof ProgramResource */ url?: string | null + /** + * + * @type {Array} + * @memberof ProgramResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -5247,6 +5313,12 @@ export interface ProgramResourceRequest { * @memberof ProgramResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof ProgramResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -5986,6 +6058,12 @@ export interface VideoPlaylistResource { * @memberof VideoPlaylistResource */ url?: string | null + /** + * + * @type {Array} + * @memberof VideoPlaylistResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -6084,6 +6162,12 @@ export interface VideoPlaylistResourceRequest { * @memberof VideoPlaylistResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof VideoPlaylistResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -6332,6 +6416,12 @@ export interface VideoResource { * @memberof VideoResource */ url?: string | null + /** + * + * @type {Array} + * @memberof VideoResource + */ + ocw_topics?: Array /** * * @type {boolean} @@ -6430,6 +6520,12 @@ export interface VideoResourceRequest { * @memberof VideoResourceRequest */ url?: string | null + /** + * + * @type {Array} + * @memberof VideoResourceRequest + */ + ocw_topics?: Array /** * * @type {string} @@ -7190,6 +7286,7 @@ export const ContentFileSearchApiAxiosParamCreator = function ( * @param {boolean | null} [dev_mode] If true return raw open search results with score explanations * @param {Array} [id] The id value for the content file * @param {number} [limit] Number of results to return per page + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -7207,6 +7304,7 @@ export const ContentFileSearchApiAxiosParamCreator = function ( dev_mode?: boolean | null, id?: Array, limit?: number, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -7253,6 +7351,10 @@ export const ContentFileSearchApiAxiosParamCreator = function ( localVarQueryParameter["limit"] = limit } + if (ocw_topic) { + localVarQueryParameter["ocw_topic"] = ocw_topic + } + if (offered_by) { localVarQueryParameter["offered_by"] = offered_by } @@ -7318,6 +7420,7 @@ export const ContentFileSearchApiFp = function (configuration?: Configuration) { * @param {boolean | null} [dev_mode] If true return raw open search results with score explanations * @param {Array} [id] The id value for the content file * @param {number} [limit] Number of results to return per page + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -7335,6 +7438,7 @@ export const ContentFileSearchApiFp = function (configuration?: Configuration) { dev_mode?: boolean | null, id?: Array, limit?: number, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -7357,6 +7461,7 @@ export const ContentFileSearchApiFp = function (configuration?: Configuration) { dev_mode, id, limit, + ocw_topic, offered_by, offset, platform, @@ -7412,6 +7517,7 @@ export const ContentFileSearchApiFactory = function ( requestParameters.dev_mode, requestParameters.id, requestParameters.limit, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -7468,6 +7574,13 @@ export interface ContentFileSearchApiContentFileSearchRetrieveRequest { */ readonly limit?: number + /** + * The ocw topic name. + * @type {Array} + * @memberof ContentFileSearchApiContentFileSearchRetrieve + */ + readonly ocw_topic?: Array + /** * The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @type {Array<'mitx' | 'ocw' | 'bootcamps' | 'xpro' | 'mitpe' | 'see'>} @@ -7551,6 +7664,7 @@ export class ContentFileSearchApi extends BaseAPI { requestParameters.dev_mode, requestParameters.id, requestParameters.limit, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -12619,6 +12733,7 @@ export const LearningResourcesSearchApiAxiosParamCreator = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -12648,6 +12763,7 @@ export const LearningResourcesSearchApiAxiosParamCreator = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -12731,6 +12847,10 @@ export const LearningResourcesSearchApiAxiosParamCreator = function ( localVarQueryParameter["min_score"] = min_score } + if (ocw_topic) { + localVarQueryParameter["ocw_topic"] = ocw_topic + } + if (offered_by) { localVarQueryParameter["offered_by"] = offered_by } @@ -12822,6 +12942,7 @@ export const LearningResourcesSearchApiFp = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -12851,6 +12972,7 @@ export const LearningResourcesSearchApiFp = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -12885,6 +13007,7 @@ export const LearningResourcesSearchApiFp = function ( limit, max_incompleteness_penalty, min_score, + ocw_topic, offered_by, offset, platform, @@ -12952,6 +13075,7 @@ export const LearningResourcesSearchApiFactory = function ( requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -13068,6 +13192,13 @@ export interface LearningResourcesSearchApiLearningResourcesSearchRetrieveReques */ readonly min_score?: number | null + /** + * The ocw topic name. + * @type {Array} + * @memberof LearningResourcesSearchApiLearningResourcesSearchRetrieve + */ + readonly ocw_topic?: Array + /** * The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @type {Array<'mitx' | 'ocw' | 'bootcamps' | 'xpro' | 'mitpe' | 'see'>} @@ -13187,6 +13318,7 @@ export class LearningResourcesSearchApi extends BaseAPI { requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -13424,6 +13556,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -13454,6 +13587,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -13538,6 +13672,10 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( localVarQueryParameter["min_score"] = min_score } + if (ocw_topic) { + localVarQueryParameter["ocw_topic"] = ocw_topic + } + if (offered_by) { localVarQueryParameter["offered_by"] = offered_by } @@ -13620,6 +13758,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -13649,6 +13788,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -13732,6 +13872,10 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( localVarQueryParameter["min_score"] = min_score } + if (ocw_topic) { + localVarQueryParameter["ocw_topic"] = ocw_topic + } + if (offered_by) { localVarQueryParameter["offered_by"] = offered_by } @@ -13810,6 +13954,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -13841,6 +13986,7 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -13926,6 +14072,10 @@ export const LearningResourcesUserSubscriptionApiAxiosParamCreator = function ( localVarQueryParameter["min_score"] = min_score } + if (ocw_topic) { + localVarQueryParameter["ocw_topic"] = ocw_topic + } + if (offered_by) { localVarQueryParameter["offered_by"] = offered_by } @@ -14079,6 +14229,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -14109,6 +14260,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -14144,6 +14296,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit, max_incompleteness_penalty, min_score, + ocw_topic, offered_by, offset, platform, @@ -14188,6 +14341,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -14217,6 +14371,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -14251,6 +14406,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit, max_incompleteness_penalty, min_score, + ocw_topic, offered_by, offset, platform, @@ -14294,6 +14450,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( * @param {number} [limit] Number of results to return per page * @param {number | null} [max_incompleteness_penalty] Maximum score penalty for incomplete OCW courses in percent. An OCW course with completeness = 0 will have this score penalty. Partially complete courses have a linear penalty proportional to the degree of incompleteness. Only affects results if there is a search term. * @param {number | null} [min_score] Minimum score value a text query result needs to have to be displayed + * @param {Array} [ocw_topic] The ocw topic name. * @param {Array} [offered_by] The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @param {number} [offset] The initial index from which to return the results * @param {Array} [platform] The platform on which the learning resource is offered * `edx` - edX * `ocw` - MIT OpenCourseWare * `oll` - Open Learning Library * `mitxonline` - MITx Online * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `csail` - CSAIL * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * `scc` - Schwarzman College of Computing * `ctl` - Center for Transportation & Logistics * `whu` - WHU * `susskind` - Susskind * `globalalumni` - Global Alumni * `simplilearn` - Simplilearn * `emeritus` - Emeritus * `podcast` - Podcast * `youtube` - YouTube @@ -14325,6 +14482,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit?: number, max_incompleteness_penalty?: number | null, min_score?: number | null, + ocw_topic?: Array, offered_by?: Array, offset?: number, platform?: Array, @@ -14358,6 +14516,7 @@ export const LearningResourcesUserSubscriptionApiFp = function ( limit, max_incompleteness_penalty, min_score, + ocw_topic, offered_by, offset, platform, @@ -14458,6 +14617,7 @@ export const LearningResourcesUserSubscriptionApiFactory = function ( requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -14501,6 +14661,7 @@ export const LearningResourcesUserSubscriptionApiFactory = function ( requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -14543,6 +14704,7 @@ export const LearningResourcesUserSubscriptionApiFactory = function ( requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -14679,6 +14841,13 @@ export interface LearningResourcesUserSubscriptionApiLearningResourcesUserSubscr */ readonly min_score?: number | null + /** + * The ocw topic name. + * @type {Array} + * @memberof LearningResourcesUserSubscriptionApiLearningResourcesUserSubscriptionCheckList + */ + readonly ocw_topic?: Array + /** * The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @type {Array<'mitx' | 'ocw' | 'bootcamps' | 'xpro' | 'mitpe' | 'see'>} @@ -14868,6 +15037,13 @@ export interface LearningResourcesUserSubscriptionApiLearningResourcesUserSubscr */ readonly min_score?: number | null + /** + * The ocw topic name. + * @type {Array} + * @memberof LearningResourcesUserSubscriptionApiLearningResourcesUserSubscriptionList + */ + readonly ocw_topic?: Array + /** * The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @type {Array<'mitx' | 'ocw' | 'bootcamps' | 'xpro' | 'mitpe' | 'see'>} @@ -15050,6 +15226,13 @@ export interface LearningResourcesUserSubscriptionApiLearningResourcesUserSubscr */ readonly min_score?: number | null + /** + * The ocw topic name. + * @type {Array} + * @memberof LearningResourcesUserSubscriptionApiLearningResourcesUserSubscriptionSubscribeCreate + */ + readonly ocw_topic?: Array + /** * The organization that offers the learning resource * `mitx` - MITx * `ocw` - MIT OpenCourseWare * `bootcamps` - Bootcamps * `xpro` - MIT xPRO * `mitpe` - MIT Professional Education * `see` - MIT Sloan Executive Education * @type {Array<'mitx' | 'ocw' | 'bootcamps' | 'xpro' | 'mitpe' | 'see'>} @@ -15197,6 +15380,7 @@ export class LearningResourcesUserSubscriptionApi extends BaseAPI { requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -15242,6 +15426,7 @@ export class LearningResourcesUserSubscriptionApi extends BaseAPI { requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, @@ -15286,6 +15471,7 @@ export class LearningResourcesUserSubscriptionApi extends BaseAPI { requestParameters.limit, requestParameters.max_incompleteness_penalty, requestParameters.min_score, + requestParameters.ocw_topic, requestParameters.offered_by, requestParameters.offset, requestParameters.platform, diff --git a/frontends/main/src/app-pages/ChannelPage/ChannelSearchFacetDisplay.tsx b/frontends/main/src/app-pages/ChannelPage/ChannelSearchFacetDisplay.tsx index 2da87b65d5..c5404b191e 100644 --- a/frontends/main/src/app-pages/ChannelPage/ChannelSearchFacetDisplay.tsx +++ b/frontends/main/src/app-pages/ChannelPage/ChannelSearchFacetDisplay.tsx @@ -8,9 +8,8 @@ import type { BooleanFacetKey, } from "@mitodl/course-search-utils" import { BOOLEAN_FACET_NAMES } from "@mitodl/course-search-utils" -import { Skeleton, styled } from "ol-components" +import { Skeleton, styled, SimpleSelect } from "ol-components" import type { SimpleSelectOption } from "ol-components" -import { StyledSelect } from "@/page-components/SearchDisplay/SearchDisplay" const StyledSkeleton = styled(Skeleton)` display: inline-flex; @@ -117,7 +116,7 @@ const AvailableFacetsDropdowns: React.FC< return ( facetItems.length && ( - , href: SEARCH_NEW, }, diff --git a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx index 98f6c45797..0e6a55bc1f 100644 --- a/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx +++ b/frontends/main/src/page-components/SearchDisplay/SearchDisplay.tsx @@ -51,10 +51,6 @@ import type { TabConfig } from "./ResourceCategoryTabs" import { ResourceCard } from "../ResourceCard/ResourceCard" import { useUserMe } from "api/hooks/user" -export const StyledSelect = styled(SimpleSelect)` - min-width: 160px; -` - const StyledResourceTabs = styled(ResourceCategoryTabs.TabList)` margin-top: 0 px; ` @@ -469,7 +465,7 @@ const SORT_OPTIONS = [ value: "", }, { - label: "New", + label: "Recently Added", value: "new", }, { @@ -601,7 +597,7 @@ const SearchDisplay: React.FC = ({ } const searchModeDropdown = ( - @@ -620,7 +616,7 @@ const SearchDisplay: React.FC = ({ ) const sortDropdown = ( - setParamValue("sortby", e.target.value)} diff --git a/frontends/mit-learn/src/page-components/FollowPopover/FollowPopover.tsx b/frontends/mit-learn/src/page-components/FollowPopover/FollowPopover.tsx new file mode 100644 index 0000000000..3d21398f11 --- /dev/null +++ b/frontends/mit-learn/src/page-components/FollowPopover/FollowPopover.tsx @@ -0,0 +1,144 @@ +import React, { useMemo } from "react" +import { Popover, Typography, styled, Button } from "ol-components" +import type { PopoverProps } from "ol-components" +import { getSearchParamMap } from "@/common/utils" + +import { SignupPopover } from "../SignupPopover/SignupPopover" + +import { useUserMe } from "api/hooks/user" +import { SourceTypeEnum } from "api" +import { + useSearchSubscriptionCreate, + useSearchSubscriptionDelete, + useSearchSubscriptionList, +} from "api/hooks/searchSubscription" + +const StyledPopover = styled(Popover)({ + width: "300px", + maxWidth: "100vw", +}) +const HeaderText = styled(Typography)(({ theme }) => ({ + color: theme.custom.colors.darkGray2, + marginBottom: "8px", + ...theme.typography.subtitle2, +})) +const BodyText = styled(Typography)(({ theme }) => ({ + ...theme.typography.body2, + color: theme.custom.colors.silverGrayDark, + marginBottom: "16px", +})) + +const Footer = styled.div({ + display: "flex", + justifyContent: "end", + gap: "16px", +}) + +interface FollowPopoverProps + extends Pick { + itemName?: string + searchParams: URLSearchParams + sourceType: SourceTypeEnum +} + +const FollowPopover: React.FC = ({ + itemName, + searchParams, + sourceType, + ...props +}) => { + const { data: user } = useUserMe() + const subscribeParams: Record = useMemo(() => { + return { source_type: sourceType, ...getSearchParamMap(searchParams) } + }, [searchParams, sourceType]) + + const subscriptionDelete = useSearchSubscriptionDelete() + const subscriptionCreate = useSearchSubscriptionCreate() + const subscriptionList = useSearchSubscriptionList(subscribeParams, { + enabled: !!user?.is_authenticated, + }) + const unsubscribe = subscriptionDelete.mutate + const subscriptionId = subscriptionList.data?.[0]?.id + + const isSubscribed = !!subscriptionId + const handleFollowAction = async (): Promise => { + props.onClose() + if (!isSubscribed) { + await subscriptionCreate.mutateAsync({ + PercolateQuerySubscriptionRequestRequest: subscribeParams, + }) + } else { + unsubscribe(subscriptionId) + } + } + + if (user?.is_authenticated && subscriptionList.isLoading) return null + if (!user) return null + if (!user?.is_authenticated) { + return + } + + if (isSubscribed) { + return ( + + + You are following {itemName} + + + Unfollow to stop getting emails for new {itemName} courses. + +
+ + +
+
+ ) + } + return ( + + Follow {itemName}? + + You will get an email when new courses are available. + + + + ) +} + +export { FollowPopover } +export type { FollowPopoverProps } diff --git a/frontends/mit-learn/webpack.config.js b/frontends/mit-learn/webpack.config.js index 7e49fd3bbb..9d48b30bd0 100644 --- a/frontends/mit-learn/webpack.config.js +++ b/frontends/mit-learn/webpack.config.js @@ -45,6 +45,11 @@ const { CSRF_COOKIE_NAME, APPZI_URL, MITOL_NOINDEX, + DEFAULT_SEARCH_MODE, + DEFAULT_SEARCH_SLOP, + DEFAULT_SEARCH_STALENESS_PENALTY, + DEFAULT_SEARCH_MINIMUM_SCORE_CUTOFF, + DEFAULT_SEARCH_MAX_INCOMPLETENESS_PENALTY, } = cleanEnv(process.env, { NODE_ENV: str({ choices: ["development", "production", "test"], @@ -124,6 +129,26 @@ const { desc: "Whether to include a noindex meta tag", default: true, }), + DEFAULT_SEARCH_SLOP: num({ + desc: "The default search slop", + default: 6, + }), + DEFAULT_SEARCH_STALENESS_PENALTY: num({ + desc: "The default search staleness penalty", + default: 2.5, + }), + DEFAULT_SEARCH_MINIMUM_SCORE_CUTOFF: num({ + desc: "The default search minimum score cutoff", + default: 0, + }), + DEFAULT_SEARCH_MAX_INCOMPLETENESS_PENALTY: num({ + desc: "The default search max incompleteness penalty", + default: 90, + }), + DEFAULT_SEARCH_MODE: str({ + desc: "The default search mode", + default: "phrase", + }), }) const MITOL_FEATURES_PREFIX = "FEATURE_" @@ -265,6 +290,11 @@ module.exports = (env, argv) => { MITOL_SUPPORT_EMAIL: JSON.stringify(MITOL_SUPPORT_EMAIL), PUBLIC_URL: JSON.stringify(PUBLIC_URL), CSRF_COOKIE_NAME: JSON.stringify(CSRF_COOKIE_NAME), + DEFAULT_SEARCH_MODE: JSON.stringify(DEFAULT_SEARCH_MODE), + DEFAULT_SEARCH_MAX_INCOMPLETENESS_PENALTY, + DEFAULT_SEARCH_MINIMUM_SCORE_CUTOFF, + DEFAULT_SEARCH_SLOP, + DEFAULT_SEARCH_STALENESS_PENALTY, }, }), ] diff --git a/learning_resources/etl/ocw.py b/learning_resources/etl/ocw.py index d776f63c82..31cac2e2fb 100644 --- a/learning_resources/etl/ocw.py +++ b/learning_resources/etl/ocw.py @@ -71,6 +71,40 @@ def parse_delivery(course_data: dict) -> list[str]: return delivery +def parse_learn_topics(course_data: dict) -> list[dict]: + """ + Parse topics. Use the "mit_learn_topics" field if it exists and isn't empty, + otherwise use and transform the "topics" field values. + + Args: + course_data (dict): The course data + + Returns: + list[dict]: The topics + """ + mitlearn_topics = course_data.get("mit_learn_topics") or [] + ocw_topics = course_data.get("topics") or [] + if mitlearn_topics: + # Should already be in the correct format + return [ + {"name": topic_name} + for topic_name in sorted( + {topic for topics in mitlearn_topics for topic in topics} + ) + ] + else: + # Topics need to be transformed + return transform_topics( + [ + {"name": topic_name} + for topic_name in sorted( + {topic for topics in ocw_topics for topic in topics} + ) + ], + OFFERED_BY["code"], + ) + + def transform_content_files( s3_resource: boto3.resource, course_prefix: str, @@ -329,10 +363,6 @@ def transform_course(course_data: dict) -> dict: readable_term = f"+{slugify(term)}" if term else "" readable_year = f"_{course_data.get('year')}" if year else "" readable_id = f"{course_data[PRIMARY_COURSE_ID]}{readable_term}{readable_year}" - topics = transform_topics( - [{"name": topic} for topics in course_data.get("topics") for topic in topics], - OFFERED_BY["code"], - ) image_src = course_data.get("image_src") return { @@ -365,7 +395,14 @@ def transform_course(course_data: dict) -> dict: is_ocw=True, ), }, - "topics": topics, + "topics": parse_learn_topics(course_data), + "ocw_topics": sorted( + { + topic_name + for topic_sublist in course_data.get("topics", []) + for topic_name in topic_sublist + } + ), "runs": [transform_run(course_data)], "resource_type": LearningResourceType.course.name, "unique_field": UNIQUE_FIELD, diff --git a/learning_resources/etl/ocw_test.py b/learning_resources/etl/ocw_test.py index d54b562ce7..7ffba2cf4c 100644 --- a/learning_resources/etl/ocw_test.py +++ b/learning_resources/etl/ocw_test.py @@ -18,11 +18,15 @@ ) from learning_resources.etl.constants import CourseNumberType, ETLSource from learning_resources.etl.ocw import ( + parse_learn_topics, transform_content_files, transform_contentfile, transform_course, ) -from learning_resources.factories import ContentFileFactory +from learning_resources.factories import ( + ContentFileFactory, + LearningResourceTopicFactory, +) from learning_resources.models import ContentFile from learning_resources.utils import ( get_s3_object_and_read, @@ -239,6 +243,14 @@ def test_transform_course( # noqa: PLR0913 ) transformed_json = transform_course(extracted_json) if expected_uid: + assert transformed_json["ocw_topics"] == [ + "Anthropology", + "Ethnography", + "Humanities", + "Philosophy", + "Political Philosophy", + "Social Science", + ] assert transformed_json["readable_id"] == expected_id assert transformed_json["etl_source"] == ETLSource.ocw.name assert transformed_json["delivery"] == expected_delivery @@ -295,3 +307,45 @@ def test_transform_course( # noqa: PLR0913 ) else: assert transformed_json is None + + +@pytest.mark.parametrize("has_learn_topics", [True, False]) +def test_parse_topics(mocker, has_learn_topics): + """Topics should be assigned correctly based on mitlearn topics if present, ocw topics if not""" + ocw_topics = [ + ["Social Science", "Anthropology", "Ethnography"], + ["Social Science", "Political Science", "International Relations"], + ] + mit_learn_topics = ( + [["Social Sciences", "Anthropology"], ["Social Sciences", "Political Science"]] + if has_learn_topics + else [] + ) + course_data = { + "topics": ocw_topics, + "mit_learn_topics": mit_learn_topics, + } + mocker.patch( + "learning_resources.etl.utils.load_offeror_topic_map", + return_value={ + "Political Philosophy": ["Philosophy"], + "Ethnography": ["Anthropology"], + "International Relations": ["Political Science"], + }, + ) + for topic in ("Social Sciences", "Anthropology", "Political Science"): + LearningResourceTopicFactory.create(name=topic) + topics_dict = parse_learn_topics(course_data) + if has_learn_topics: + assert topics_dict == [ + {"name": "Anthropology"}, + {"name": "Political Science"}, + {"name": "Social Sciences"}, + ] + else: + assert topics_dict == [ + {"name": "Anthropology"}, + {"name": "Anthropology"}, + {"name": "Political Science"}, + {"name": "Political Science"}, + ] diff --git a/learning_resources/migrations/0069_learningresource_ocw_topics.py b/learning_resources/migrations/0069_learningresource_ocw_topics.py new file mode 100644 index 0000000000..3ee3678b5a --- /dev/null +++ b/learning_resources/migrations/0069_learningresource_ocw_topics.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2024-09-23 18:04 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("learning_resources", "0068_learningresource_format_pace"), + ] + + operations = [ + migrations.AddField( + model_name="learningresource", + name="ocw_topics", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=128), + blank=True, + default=list, + size=None, + ), + ), + ] diff --git a/learning_resources/models.py b/learning_resources/models.py index 4b74e723cd..e3c3bf3d75 100644 --- a/learning_resources/models.py +++ b/learning_resources/models.py @@ -411,6 +411,7 @@ class LearningResource(TimestampedModel): choices=((member.name, member.value) for member in LearningResourceType), ) topics = models.ManyToManyField(LearningResourceTopic) + ocw_topics = ArrayField(models.CharField(max_length=128), default=list, blank=True) offered_by = models.ForeignKey( LearningResourceOfferor, null=True, on_delete=models.SET_NULL ) diff --git a/learning_resources/serializers_test.py b/learning_resources/serializers_test.py index bc68711f96..b454de5ee7 100644 --- a/learning_resources/serializers_test.py +++ b/learning_resources/serializers_test.py @@ -254,6 +254,7 @@ def test_learning_resource_serializer( # noqa: PLR0913 serializers.LearningResourceTopicSerializer(topic).data for topic in resource.topics.all() ], + "ocw_topics": sorted(resource.ocw_topics), "runs": [ serializers.LearningResourceRunSerializer(instance=run).data for run in resource.runs.all() diff --git a/learning_resources_search/constants.py b/learning_resources_search/constants.py index a4208aaf4e..6bcc47e1f0 100644 --- a/learning_resources_search/constants.py +++ b/learning_resources_search/constants.py @@ -71,6 +71,7 @@ class FilterConfig: "run_id": FilterConfig("run_id", case_sensitive=True), "resource_id": FilterConfig("resource_id", case_sensitive=True), "topic": FilterConfig("topics.name"), + "ocw_topic": FilterConfig("ocw_topics"), "level": FilterConfig("runs.level.code"), "department": FilterConfig("departments.department_id"), "platform": FilterConfig("platform.code"), @@ -184,6 +185,7 @@ class FilterConfig: "channel_url": {"type": "keyword"}, }, }, + "ocw_topics": {"type": "keyword"}, "offered_by": { "type": "nested", "properties": { diff --git a/learning_resources_search/serializers.py b/learning_resources_search/serializers.py index fd9bd3fdcf..b340605169 100644 --- a/learning_resources_search/serializers.py +++ b/learning_resources_search/serializers.py @@ -276,6 +276,11 @@ class SearchRequestSerializer(serializers.Serializer): child=serializers.CharField(), help_text="The topic name. To see a list of options go to api/v1/topics/", ) + ocw_topic = serializers.ListField( + required=False, + child=serializers.CharField(), + help_text="The ocw topic name.", + ) dev_mode = serializers.BooleanField( required=False, allow_null=True, diff --git a/main/settings.py b/main/settings.py index c4e3864445..8941615e60 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.19.3" +VERSION = "0.19.4" log = logging.getLogger() diff --git a/test_json/courses/16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006/data.json b/test_json/courses/16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006/data.json index 542e96fb1b..c676ac7f6e 100644 --- a/test_json/courses/16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006/data.json +++ b/test_json/courses/16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006/data.json @@ -85,12 +85,12 @@ "Exams with Solutions" ], "topics": [ - ["Engineering", "Aerospace Engineering", "Materials Selection"], - ["Engineering", "Aerospace Engineering", "Propulsion Systems"], - ["Science", "Physics", "Thermodynamics"], - ["Engineering", "Mechanical Engineering", "Fluid Mechanics"], - ["Engineering", "Aerospace Engineering"], - ["Business", "Project Management"] + ["Social Science", "Anthropology", "Ethnography"], + ["Humanities", "Philosophy", "Political Philosophy"] + ], + "mit_learn_topics": [ + ["Social Sciences", "Anthropology"], + ["Social Sciences", "Political Science"] ], "primary_course_number": "16.01", "extra_course_numbers": "16.02, 16.03, 16.04",