Skip to content
Closed
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
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ MIT Learn follows the same [initial setup steps outlined in the common OL web ap
Run through those steps **including the addition of `/etc/hosts` aliases and the optional step for running the
`createsuperuser` command**.

For `/etc/hosts`, you'll need to add entries for the following domains if you are relying on the sample environment variables:
```
api.open.odl.local
open.odl.local
kc.ol.local
```

### Configuration

Configuration can be put in the following files which are gitignored:
Expand All @@ -26,7 +33,7 @@ mit-learn/
├── env/
│ ├── shared.local.env (provided to both frontend and backend containers)
│ ├── frontend.local.env (provided only to frontend containers)
│ └── backend.local.env (provided only to frontend containers)
│ └── backend.local.env (provided only to backend containers)
└── .env (legacy file)
```

Expand All @@ -43,6 +50,16 @@ The following settings must be configured before running the app:
is not needed. It's recommended that you eventually configure the site to be able
to send emails. Those configuration steps can be found [below](#enabling-email).

Before proceeding with any additional setup, you may want to adjust your docker settings to allow more memory to be used by the containers. Many engineers allocate the bulk of their system resources by navigating to Settings -> Resources in Docker Desktop.
Additionally, the `web` and `celery` services specify `memory_limit` values, which you can adjust using the following environment variables:
```
MITOL_CELERY_MEM_LIMIT
MITOL_WEB_MEM_LIMIT
```

If any resource limits are set too low, you may encounter OOMKilled errors in the logs of those services. The primary way this will manifest is that containers may unexpectedly die during operation resulting in 500 errors or unusual celery task execution.
Given a container that is unexpectedly down, you can verify that it was OOMKilled by running `docker inspect <container_name> -f '{{json .State.OOMKilled}}'`, or by checking the Docker VM kernel message logs for relevant output.

### Loading Data

The MIT Learn platform aggregates data from many sources. These data are populated by ETL (extract, transform, load) pipelines that run automatically on a regular schedule. Django [management commands](https://docs.djangoproject.com/en/4.2/howto/custom-management-commands/) are also available to force the pipelines to run—particularly useful for local development.
Expand All @@ -62,12 +79,21 @@ docker compose run --rm web python manage.py backpopulate_xpro_data

See [learning_resources/management/commands](learning_resources/management/commands) and [main/settings_course_etl.py](main/settings_course_etl.py) for more ETL commands and their relevant environment variables.

You may also want to generate embeddings once you have populated some data in so vector search and recommendations function as expected. See [docs/how-to/embeddings.md](docs/how-to/embeddings.md) for more details on this process.

### Frontend Development

The frontend package root is at [./frontends](./frontends). A `watch` container is provided to serve and rebuild the front end when there are changes to source files, which is started alongside backing services with `docker compose up`.

Package scripts are also provided for building and serving the frontend in isolation. More detail can be found in the [Frontend README](./frontends/README.md#frontend-development).

### Connecting with Keycloak for authentication

While not technically required to get the app running, it is the preferred way to work on features which require authentication.

Please read [the Keycloak README](README-keycloak.md) for instructions on authenticating via
local Keycloak and APISIX containers.

## Code Generation

MIT Learn uses [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/) to generate and OpenAPI spec from Django views. Additionally, we use [OpenAPITools/openapi-generator](https://github.com/OpenAPITools/openapi-generator) to generate Typescript declarations and an API Client. These generated files are checked into source control; CI checks that they are up-to-date. To regenerate these files, run
Expand Down Expand Up @@ -211,11 +237,6 @@ This repo includes a config for running a [Jupyter notebook](https://jupyter.org

From there, you should be able to run code snippets with a live Django app just like you would in a Django shell.

### Connecting with Keycloak for authentication

Please read [the Keycloak README](README-keycloak.md) for instructions on authenticating via
local Keycloak and APISIX containers.

### Configuring PostHog Support

The system can use PostHog to evaluate feature flags and record views for the Learning Resource drawer.
Expand Down
11 changes: 11 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Release Notes
=============

Version 0.41.7
--------------

- Update dependency @sentry/nextjs to v10 (#2462)
- Update smoot version (#2459)
- Update dependency lxml to v6 (#2465)
- Update dependency tiktoken to ^0.11.0 (#2464)
- Minor tweaks to initial setup instructions, add memory env var for web container, note minimum required domain config for things to "work out of box" (#2458)
- Add (citation) urls for canvas contentfiles (#2457)
- fix one-click enrollment for the same course across multiple orgs (#2453)

Version 0.41.5 (Released August 25, 2025)
--------------

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
context: .
dockerfile: Dockerfile
target: final
mem_limit: 1gb
mem_limit: ${MITOL_WEB_MEM_LIMIT:-2gb}
cpus: 2
command: ./scripts/run-django-dev.sh
stdin_open: true
Expand Down Expand Up @@ -54,7 +54,7 @@ services:
context: .
dockerfile: Dockerfile
target: final
mem_limit: ${MITOL_CELERY_MEM_LIMIT:-2gb}
mem_limit: ${MITOL_CELERY_MEM_LIMIT:-4gb}
cpus: ${MITOL_CELERY_CPU_LIMIT:-2}
command: >
/bin/bash -c '
Expand Down
2 changes: 1 addition & 1 deletion docs/how-to/embeddings.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The following embeddings related settings are available in the `settings.py` fil

## Embeddings for New Content

Embeddings are automatically generated for new conetnt by a periodic celery task. The tasks are defined `vector_search/tasks.py`.
Embeddings are automatically generated for new content by a periodic celery task. The tasks are defined `vector_search/tasks.py`.

## Embeddings for Existing Content

Expand Down
3 changes: 0 additions & 3 deletions env/backend.local.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
MAILGUN_SENDER_DOMAIN=open.odl.local
MAILGUN_KEY=fake

# Set this to False if apisix/keycloak are running locally
DISABLE_APISIX_USER_MIDDLEWARE=True

# APISIX/Keycloak settings
APISIX_LOGOUT_URL=http://api.open.odl.local:8065/logout/
APISIX_SESSION_SECRET_KEY=supertopsecret1234
Expand Down
2 changes: 1 addition & 1 deletion env/shared.local.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# EMBEDLY_KEY=
# Use port 8063 if apisix/keycloak disabled
MITOL_API_BASE_URL=http://api.open.odl.local:8063
MITOL_API_DOMAIN = api.open.odl.local # dev only, should match domain of above
MITOL_API_DOMAIN=api.open.odl.local # dev only, should match domain of above
3 changes: 3 additions & 0 deletions frontends/api/src/mitxonline/hooks/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { contractQueries } from "./queries"

export { contractQueries }
36 changes: 36 additions & 0 deletions frontends/api/src/mitxonline/hooks/contracts/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { queryOptions } from "@tanstack/react-query"
import type {
B2bApiB2bContractsRetrieveRequest,
ContractPage,
} from "@mitodl/mitxonline-api-axios/v2"

import { b2bApi } from "../../clients"

const contractKeys = {
root: ["mitxonline", "contracts"],
contractsList: () => [...contractKeys.root, "list"],
contractDetail: (opts: B2bApiB2bContractsRetrieveRequest) => [
...contractKeys.root,
"detail",
opts,
],
}

const contractQueries = {
contractsList: () =>
queryOptions({
queryKey: contractKeys.contractsList(),
queryFn: async (): Promise<ContractPage[]> => {
return b2bApi.b2bContractsList().then((res) => res.data)
},
}),
contractDetail: (opts: B2bApiB2bContractsRetrieveRequest) =>
queryOptions({
queryKey: contractKeys.contractDetail(opts),
queryFn: async (): Promise<ContractPage> => {
return b2bApi.b2bContractsRetrieve(opts).then((res) => res.data)
},
}),
}

export { contractQueries, contractKeys }
5 changes: 5 additions & 0 deletions frontends/api/src/mitxonline/test-utils/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const b2bAttach = {
b2bAttachView: (code: string) => `${API_BASE_URL}/api/v0/b2b/attach/${code}/`,
}

const contracts = {
contractsList: () => `${API_BASE_URL}/api/v0/b2b/contracts/`,
}

export {
b2b,
b2bAttach,
Expand All @@ -64,4 +68,5 @@ export {
courses,
organization,
programEnrollments,
contracts,
}
1 change: 1 addition & 0 deletions frontends/main/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const config: Config.InitialOptions = {
"^rehype-mathjax/browser$": "ol-test-utilities/filemocks/filemock.js",
"^rehype-raw$": "ol-test-utilities/filemocks/filemock.js",
"^remark-math$": "ol-test-utilities/filemocks/filemock.js",
"^remark-supersub$": "ol-test-utilities/filemocks/filemock.js",
},
}
export default config
4 changes: 2 additions & 2 deletions frontends/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"@emotion/styled": "^11.11.0",
"@mitodl/course-search-utils": "3.3.2",
"@mitodl/mitxonline-api-axios": "2025.8.12",
"@mitodl/smoot-design": "^6.10.0",
"@mitodl/smoot-design": "^6.17.0",
"@next/bundle-analyzer": "^14.2.15",
"@remixicon/react": "^4.2.0",
"@sentry/nextjs": "^9.0.0",
"@sentry/nextjs": "^10.0.0",
"@tanstack/react-query": "^5.66",
"api": "workspace:*",
"classnames": "^2.5.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,14 @@ describe.each([
case: "current",
},
{
course: futureDashboardCourse(),
course: futureDashboardCourse({
enrollment: {
status: EnrollmentStatus.Enrolled,
mode: EnrollmentMode.Audit,
},
}),
expected: { enabled: false },
label: "future",
case: "future",
},
])(
"Courseware CTA and is enabled/disabled (enabled=$expected.enabled) based on date (case: $case)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const CoursewareButton = styled(
courseNoun,
...others
}: CoursewareButtonProps) => {
const children = getCoursewareText({
const coursewareText = getCoursewareText({
endDate,
courseNoun,
enrollmentStatus,
Expand All @@ -162,8 +162,8 @@ const CoursewareButton = styled(
const createEnrollment = useCreateEnrollment({
readable_id: coursewareId ?? "",
})
return hasStarted && href ? (
hasEnrolled ? (
return (hasStarted && href) || !hasEnrolled ? (
hasEnrolled && href ? (
<ButtonLink
size="small"
variant="primary"
Expand All @@ -172,7 +172,7 @@ const CoursewareButton = styled(
className={className}
{...others}
>
{children}
{coursewareText}
</ButtonLink>
) : (
<Button
Expand All @@ -185,7 +185,7 @@ const CoursewareButton = styled(
}}
{...others}
>
{children}
{coursewareText}
{createEnrollment.isPending && (
<SpinnerContainer>
<LoadingSpinner loading={createEnrollment.isPending} size={16} />
Expand All @@ -202,7 +202,7 @@ const CoursewareButton = styled(
className={className}
{...others}
>
{children}
{coursewareText}
</Button>
)
},
Expand Down Expand Up @@ -364,7 +364,7 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
{enrollment?.mode !== EnrollmentMode.Verified && offerUpgrade ? (
<UpgradeBanner
data-testid="upgrade-root"
canUpgrade={run.canUpgrade}
canUpgrade={run.canUpgrade ?? false}
certificateUpgradeDeadline={run.certificateUpgradeDeadline}
certificateUpgradePrice={run.certificateUpgradePrice}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
theme,
} from "ol-components"
import { useQuery } from "@tanstack/react-query"
import { mitxonlineEnrollments } from "./transform"
import { mitxonlineEnrollmentsToDashboardCourses } from "./transform"
import { DashboardCard } from "./DashboardCard"
import { DashboardCourse, EnrollmentStatus } from "./types"
import { MaybeHasStatusAndDetail } from "@/app/getQueryClient"
Expand Down Expand Up @@ -176,7 +176,7 @@ const EnrollmentExpandCollapse: React.FC<EnrollmentExpandCollapseProps> = ({
const EnrollmentDisplay = () => {
const { data: enrolledCourses, isLoading } = useQuery({
...enrollmentQueries.courseRunEnrollmentsList(),
select: mitxonlineEnrollments,
select: mitxonlineEnrollmentsToDashboardCourses,
throwOnError: (error) => {
const err = error as MaybeHasStatusAndDetail
const status = err?.response?.status
Expand Down
Loading
Loading