Skip to content

Release 0.31.5 #2252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: release
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Install poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1
with:
version: 1.8.5
version: 2.1.3
virtualenvs-create: true
virtualenvs-in-project: true

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/openapi-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ jobs:
>> $GITHUB_OUTPUT
- name: Find existing comment
id: find_comment
uses: peter-evans/find-comment@v3
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: ${{ github.repository }}
issue-number: ${{ github.event.pull_request.number }}
body-includes: "## OpenAPI Changes"
- name: Post changes as comment
uses: peter-evans/create-or-update-comment@v3
uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
# Even if no changes, make sure to update old comment if it was found.
if: steps.oasdif_summary.outputs.summary || steps.find_comment.outputs.comment-id
with:
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repos:
hooks:
- id: shfmt
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.0
rev: v1.37.1
hooks:
- id: yamllint
args: [--format, parsable, -d, relaxed]
Expand Down Expand Up @@ -78,7 +78,7 @@ repos:
- "config/keycloak/realms/ol-local-realm.json"
additional_dependencies: ["gibberish-detector"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.11.6"
rev: "v0.11.9"
hooks:
- id: ruff-format
- id: ruff
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ ENV \
PYTHONDONTWRITEBYTECODE=1 \
PIP_DISABLE_PIP_VERSION_CHECK=on \
POETRY_NO_INTERACTION=1 \
POETRY_VERSION=1.8.5 \
POETRY_VERSION=2.1.3 \
POETRY_VIRTUALENVS_CREATE=true \
POETRY_CACHE_DIR='/tmp/cache/poetry' \
POETRY_HOME='/home/mitodl/.local' \
Expand Down
16 changes: 16 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
Release Notes
=============

Version 0.31.5
--------------

- config: Allow health endpoints to bypass SSL redirect
- config: Remove syslog handler (#2250)
- feat: Add health check (#2247)
- Ensure celery tasks execute once (#2242)
- Fix flaky test (#2246)
- [pre-commit.ci] pre-commit autoupdate (#2248)
- Set card link on org dashboard pages to courseware url (#2239)
- [pre-commit.ci] pre-commit autoupdate (#2215)
- chore(deps): pin dependencies (#2245)
- fix(deps): update dependency youtube-transcript-api to v1 (#2206)
- Tracking user account creation event in posthog (#2237)
- Fix ordering of modules in UAI dashboard (#2232)

Version 0.31.3 (Released May 12, 2025)
--------------

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ services:
command: >
/bin/bash -c '
sleep 3;
celery -A main.celery:app worker -E -Q default,edx_content -B -l ${MITOL_LOG_LEVEL:-INFO}'
celery -A main.celery:app worker -E -Q default,edx_content -B --scheduler redbeat.RedBeatScheduler -l ${MITOL_LOG_LEVEL:-INFO}'
depends_on:
db:
condition: service_healthy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ type DashboardCardProps = {
offerUpgrade?: boolean
contextMenuItems?: SimpleMenuItem[]
isLoading?: boolean
titleHref?: string | null
buttonHref?: string | null
}
const DashboardCard: React.FC<DashboardCardProps> = ({
dashboardResource,
Expand All @@ -273,6 +275,8 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
offerUpgrade = true,
contextMenuItems = [],
isLoading = false,
titleHref,
buttonHref,
}) => {
const { title, marketingUrl, enrollment, run } = dashboardResource
const titleSection = isLoading ? (
Expand All @@ -283,7 +287,11 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
</>
) : (
<>
<TitleLink size="medium" color="black" href={marketingUrl}>
<TitleLink
size="medium"
color="black"
href={titleHref ? titleHref : marketingUrl}
>
{title}
</TitleLink>
{enrollment?.status === EnrollmentStatus.Completed ? (
Expand Down Expand Up @@ -313,7 +321,7 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
<CoursewareButton
data-testid="courseware-button"
startDate={run.startDate}
href={run.coursewareUrl}
href={buttonHref ? buttonHref : run.coursewareUrl}
endDate={run.endDate}
courseNoun={courseNoun}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import {
EnrollmentStatus,
} from "./types"
import type { DashboardCourse } from "./types"
import * as u from "api/test-utils"
import { urls, factories } from "api/mitxonline-test-utils"
import { setMockResponse } from "../../../test-utils"
import { mockOrgData } from "api/mitxonline-hooks/enrollment"

const makeCourses = factories.courses.courses
const makeProgram = factories.programs.program

const dashboardCourse: PartialFactory<DashboardCourse> = (...overrides) => {
return mergeOverrides<DashboardCourse>(
Expand All @@ -31,4 +38,39 @@ const dashboardCourse: PartialFactory<DashboardCourse> = (...overrides) => {
)
}

export { dashboardCourse }
const setupProgramsAndCourses = () => {
const user = u.factories.user.user()
setMockResponse.get(u.urls.userMe.get(), user)

const orgId = mockOrgData.orgX.id
const coursesA = makeCourses({ count: 4 })
const coursesB = makeCourses({ count: 3 })
const programA = makeProgram({
courses: coursesA.results.map((c) => c.id),
})
const programB = makeProgram({
courses: coursesB.results.map((c) => c.id),
})

setMockResponse.get(
urls.programs.programsList({ orgId: mockOrgData.orgX.id }),
{ results: [programA, programB] },
)
setMockResponse.get(urls.courses.coursesList({ id: programA.courses }), {
results: coursesA.results,
})
setMockResponse.get(urls.courses.coursesList({ id: programB.courses }), {
results: coursesB.results,
})

return {
orgId,
user,
programA,
programB,
coursesA: coursesA.results,
coursesB: coursesB.results,
}
}

export { dashboardCourse, setupProgramsAndCourses }
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { factories as mitx } from "api/mitxonline-test-utils"
import * as transform from "./transform"
import { DashboardResourceType, EnrollmentStatus } from "./types"
import type { DashboardResource } from "./types"
import {
mitxonlineCourses,
mitxonlineProgram,
sortDashboardCourses,
} from "./transform"
import { setupProgramsAndCourses } from "./test-utils"

describe("Transforming mitxonline enrollment data to DashboardResource", () => {
test.each([
Expand Down Expand Up @@ -53,4 +59,33 @@ describe("Transforming mitxonline enrollment data to DashboardResource", () => {
expect(transformed).toHaveLength(1)
expect(transformed[0].run.certificateUpgradePrice).toEqual("10")
})

test("sortDashboardCourses sorts courses by enrollment status and program order", () => {
const { programA, coursesA } = setupProgramsAndCourses()

const enrollments = [
mitx.enrollment.courseEnrollment({
run: { course: { id: coursesA[0].id } },
grades: [mitx.enrollment.grade({ passed: true })],
enrollment_mode: "audit",
}),
mitx.enrollment.courseEnrollment({
run: { course: { id: coursesA[1].id } },
grades: [],
enrollment_mode: "verified",
}),
]

const sortedCourses = sortDashboardCourses(
mitxonlineProgram(programA),
mitxonlineCourses({ courses: coursesA, enrollments }),
)

expect(sortedCourses.map((course) => course.id)).toEqual([
`mitxonline-course-${coursesA[1].id}`, // Enrolled course
`mitxonline-course-${coursesA[0].id}`, // Completed course
`mitxonline-course-${coursesA[2].id}`, // Not enrolled course
`mitxonline-course-${coursesA[3].id}`, // Not enrolled course
])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,34 @@ const mitxonlineProgram = (raw: V2Program): DashboardProgram => {
}
}

export { mitxonlineEnrollments, mitxonlineCourses, mitxonlineProgram }
const sortDashboardCourses = (
program: DashboardProgram,
courses: DashboardCourse[],
) => {
return courses.sort((a, b) => {
const aCompleted = a.enrollment?.status === EnrollmentStatus.Completed
const bCompleted = b.enrollment?.status === EnrollmentStatus.Completed
const aEnrolled = a.enrollment?.status === EnrollmentStatus.Enrolled
const bEnrolled = b.enrollment?.status === EnrollmentStatus.Enrolled
if (aEnrolled && !bEnrolled) {
return -1
}
if (!aEnrolled && bEnrolled) {
return 1
}
if (aCompleted && !bCompleted) {
return -1
}
if (!aCompleted && bCompleted) {
return 1
}
return 0
})
}

export {
mitxonlineEnrollments,
mitxonlineCourses,
mitxonlineProgram,
sortDashboardCourses,
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React from "react"
import { renderWithProviders, screen, within } from "@/test-utils"
import OrganizationContent from "./OrganizationContent"
import * as u from "api/test-utils"
import { setMockResponse } from "api/test-utils"
import { urls, factories } from "api/mitxonline-test-utils"
import { mockOrgData } from "api/mitxonline-hooks/enrollment"
import { useFeatureFlagEnabled } from "posthog-js/react"
import {
mitxonlineCourses,
mitxonlineProgram,
sortDashboardCourses,
} from "./CoursewareDisplay/transform"
import { setupProgramsAndCourses } from "./CoursewareDisplay/test-utils"

const makeCourseEnrollment = factories.enrollment.courseEnrollment
const makeGrade = factories.enrollment.grade
const makeProgram = factories.programs.program
const makeCourses = factories.courses.courses

jest.mock("posthog-js/react")
const mockedUseFeatureFlagEnabled = jest
Expand All @@ -22,41 +25,6 @@ describe("OrganizationContent", () => {
mockedUseFeatureFlagEnabled.mockReturnValue(true)
})

const setupProgramsAndCourses = () => {
const user = u.factories.user.user()
setMockResponse.get(u.urls.userMe.get(), user)

const orgId = mockOrgData.orgX.id
const coursesA = makeCourses({ count: 4 })
const coursesB = makeCourses({ count: 3 })
const programA = makeProgram({
courses: coursesA.results.map((c) => c.id),
})
const programB = makeProgram({
courses: coursesB.results.map((c) => c.id),
})

setMockResponse.get(
urls.programs.programsList({ orgId: mockOrgData.orgX.id }),
{ results: [programA, programB] },
)
setMockResponse.get(urls.courses.coursesList({ id: programA.courses }), {
results: coursesA.results,
})
setMockResponse.get(urls.courses.coursesList({ id: programB.courses }), {
results: coursesB.results,
})

return {
orgId,
user,
programA,
programB,
coursesA: coursesA.results,
coursesB: coursesB.results,
}
}

it("displays a header for each program returned and cards for courses in program", async () => {
const { orgId, programA, programB, coursesA, coursesB } =
setupProgramsAndCourses()
Expand All @@ -82,7 +50,7 @@ describe("OrganizationContent", () => {
})

test("Shows correct enrollment status", async () => {
const { orgId, coursesA } = setupProgramsAndCourses()
const { orgId, programA, coursesA } = setupProgramsAndCourses()
const enrollments = [
makeCourseEnrollment({
run: { course: { id: coursesA[0].id, title: coursesA[0].title } },
Expand All @@ -104,15 +72,19 @@ describe("OrganizationContent", () => {
"enrollment-card-desktop",
)
expect(cards.length).toBeGreaterThan(0)
const sortedCourses = sortDashboardCourses(
mitxonlineProgram(programA),
mitxonlineCourses({ courses: coursesA, enrollments: enrollments }),
)
cards.forEach((card, i) => {
const course = coursesA[i]
const course = sortedCourses[i]
expect(card).toHaveTextContent(course.title)
const indicator = within(card).getByTestId("enrollment-status")

if (i === 0) {
expect(indicator).toHaveTextContent("Completed")
} else if (i === 1) {
expect(indicator).toHaveTextContent("Enrolled")
} else if (i === 1) {
expect(indicator).toHaveTextContent("Completed")
} else {
expect(indicator).toHaveTextContent("Not Enrolled")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,15 @@ const OrgProgramDisplay: React.FC<{
<Typography variant="body1">{program.description}</Typography>
</ProgramHeader>
<PlainList itemSpacing={0}>
{courses.map((course) => (
{transform.sortDashboardCourses(program, courses).map((course) => (
<DashboardCardStyled
Component="li"
key={course.id}
dashboardResource={course}
courseNoun="Module"
offerUpgrade={false}
titleHref={course.run?.coursewareUrl}
buttonHref={course.run?.coursewareUrl}
/>
))}
</PlainList>
Expand Down
Loading
Loading