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
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ repos:
pass_filenames: false
always_run: true
- repo: https://github.com/scop/pre-commit-shfmt
rev: v3.13.1-1
rev: v3.14.1-1
hooks:
- id: shfmt
- repo: https://github.com/adrienverge/yamllint.git
Expand Down Expand Up @@ -90,7 +90,7 @@ repos:
- "config/keycloak/realms/ol-local-realm.json"
additional_dependencies: ["gibberish-detector"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.15.21"
rev: "v0.16.8"
hooks:
- id: ruff-format
- id: ruff
Expand Down Expand Up @@ -118,12 +118,12 @@ repos:
exclude: node_modules/
require_serial: false
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
rev: v0.11.0.1-1
hooks:
- id: shellcheck
args: ["--severity=warning"]
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.29.0
rev: v1.30.1
hooks:
- id: zizmor
args: [--no-progress, --min-severity=medium, --min-confidence=medium]
Expand Down
12 changes: 12 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Release Notes
=============

Version 0.81.2
--------------

- Fix OLL archive content ingestion (#3973)
- Read the warehouse catalog and schema from settings (#4005)
- Fall back to the original image before the default image (#4007)
- feat(website-content): require SEO fields to publish, and align the published control bar (#4009)
- Do not follow redirects when fetching OVS transcripts (#4003)
- fix(learning_resources): gate summary/flashcards like content on ContentFileViewSet (#3999)
- fix(widgets): remove the unused RSS Feed widget type (#4000)
- Update pre-commit hooks and adapt to ruff 0.16 (#3993)

Version 0.81.1
--------------

Expand Down
7 changes: 4 additions & 3 deletions docs/articles-cdn-purge.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ article = Article.objects.create(
title="New Article",
content={"type": "doc", "content": []},
is_published=True,
user=some_user
user=some_user,
)
# CDN purge is automatically queued!

Expand All @@ -168,7 +168,7 @@ You can manually trigger CDN purges:
from articles.tasks import (
fastly_purge_relative_url,
fastly_purge_articles_list,
fastly_full_purge
fastly_full_purge,
)

# Purge a specific URL immediately (blocking)
Expand All @@ -192,7 +192,7 @@ For backwards compatibility, the following aliases are available but deprecated:
# Old names (still work but discouraged)
from articles.tasks import (
queue_fastly_purge_articles_list, # Use fastly_purge_articles_list
queue_fastly_full_purge, # Use fastly_full_purge
queue_fastly_full_purge, # Use fastly_full_purge
)
```

Expand Down Expand Up @@ -260,6 +260,7 @@ All CDN purge operations are logged using Python's standard logging:

```python
import logging

logger = logging.getLogger("fastly_purge")
```

Expand Down
67 changes: 37 additions & 30 deletions docs/how-to/articles-to-news.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,23 +144,27 @@ Article.objects.filter(is_published=True)
### 2. Transform

```python
[{
'title': 'MIT Learn Articles',
'url': '/articles',
'feed_type': 'news',
'items': [{
'guid': 'article-1',
'title': 'My Article',
'url': '/articles/my-article',
'summary': 'First 500 chars...',
'content': 'Full text...',
'detail': {
'authors': ['John Doe'],
'topics': [],
'publish_date': '2024-01-01T00:00:00Z',
}
}]
}]
[
{
"title": "MIT Learn Articles",
"url": "/articles",
"feed_type": "news",
"items": [
{
"guid": "article-1",
"title": "My Article",
"url": "/articles/my-article",
"summary": "First 500 chars...",
"content": "Full text...",
"detail": {
"authors": ["John Doe"],
"topics": [],
"publish_date": "2024-01-01T00:00:00Z",
},
}
],
}
]
```

### 3. Load
Expand Down Expand Up @@ -190,21 +194,20 @@ The `extract_text_from_content()` function needs customization based on your JSO
```python
def extract_text_from_content(content_json: dict) -> str:
# For Draft.js
blocks = content_json.get('blocks', [])
return ' '.join([block.get('text', '') for block in blocks])
blocks = content_json.get("blocks", [])
return " ".join([block.get("text", "") for block in blocks])

# For ProseMirror
def walk_nodes(node):
if node.get('type') == 'text':
return node.get('text', '')
children = node.get('content', [])
return ' '.join(walk_nodes(child) for child in children)
if node.get("type") == "text":
return node.get("text", "")
children = node.get("content", [])
return " ".join(walk_nodes(child) for child in children)

return walk_nodes(content_json)
# For EditorJS
blocks = content_json.get('blocks', [])
return ' '.join([
block.get('data', {}).get('text', '')
for block in blocks
])
blocks = content_json.get("blocks", [])
return " ".join([block.get("data", {}).get("text", "") for block in blocks])
```

### 2. Add Image Support
Expand Down Expand Up @@ -235,7 +238,8 @@ If you add topics to your Article model:
```python
class Article(TimestampedModel):
# ... existing fields ...
topics = models.ManyToManyField('Topic')
topics = models.ManyToManyField("Topic")


# In transform_items:
entry = {
Expand Down Expand Up @@ -294,7 +298,7 @@ article = Article.objects.create(
title="Test Article",
content={"blocks": [{"text": "Test content"}]},
user=user,
is_published=True
is_published=True,
)
```

Expand All @@ -317,6 +321,7 @@ result = pipelines.articles_news_etl()

# Check results
from news_events.models import FeedSource

source = FeedSource.objects.get(title="MIT Learn Articles")
print(f"Found {source.feed_items.count()} articles in news feed")
```
Expand All @@ -340,6 +345,7 @@ print(f"Found {source.feed_items.count()} articles in news feed")
3. **Check for errors:**
```python
from news_events.tasks import get_articles_news

get_articles_news() # Run synchronously to see errors
```

Expand All @@ -350,6 +356,7 @@ print(f"Found {source.feed_items.count()} articles in news feed")
- Add debug logging:
```python
import logging

log = logging.getLogger(__name__)
log.info(f"Content structure: {content_json}")
```
Expand Down
7 changes: 1 addition & 6 deletions frontends/api/src/generated/v0/api.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { factories } from "api/mitxonline-test-utils"
import { DisplayModeEnum } from "@mitodl/mitxonline-api-axios/v2"
import { renderWithProviders } from "@/test-utils"
import { DEFAULT_RESOURCE_IMG } from "ol-utilities"
import { getByImageSrc } from "ol-test-utilities"
import { getByImageSrc, queryByImageSrc } from "ol-test-utilities"
import type { MitxOnlineResourceCardProps } from "./MitxOnlineResourceCard"

const renderCard = (props: MitxOnlineResourceCardProps) =>
Expand Down Expand Up @@ -163,7 +163,7 @@ describe("MitxOnlineResourceCard", () => {
})

describe("image error fallback", () => {
test("falls back to DEFAULT_RESOURCE_IMG when course image returns 404", () => {
test("falls back to the original, then DEFAULT_RESOURCE_IMG, when course image fails", () => {
const course = factories.courses.course({
page: {
feature_image_src: "https://example.com/course.jpg",
Expand All @@ -175,13 +175,25 @@ describe("MitxOnlineResourceCard", () => {
resourceType: "course",
href: "/test",
})
const raw = { nextJsOriginalSrc: false }
expect(
queryByImageSrc(view.container, "https://example.com/course.jpg", raw),
).toBeNull()
// Optimized image fails: retry the original, loaded directly
fireEvent.error(
getByImageSrc(view.container, "https://example.com/course.jpg"),
)
const original = getByImageSrc(
view.container,
"https://example.com/course.jpg",
raw,
)
// Original fails too: use the default
fireEvent.error(original)
getByImageSrc(view.container, DEFAULT_RESOURCE_IMG)
})

test("falls back to DEFAULT_RESOURCE_IMG when program image returns 404", () => {
test("falls back to the original, then DEFAULT_RESOURCE_IMG, when program image fails", () => {
const program = factories.programs.program({
page: {
feature_image_src: "https://example.com/program.jpg",
Expand All @@ -193,9 +205,21 @@ describe("MitxOnlineResourceCard", () => {
resourceType: "program",
href: "/test",
})
const raw = { nextJsOriginalSrc: false }
expect(
queryByImageSrc(view.container, "https://example.com/program.jpg", raw),
).toBeNull()
// Optimized image fails: retry the original, loaded directly
fireEvent.error(
getByImageSrc(view.container, "https://example.com/program.jpg"),
)
const original = getByImageSrc(
view.container,
"https://example.com/program.jpg",
raw,
)
// Original fails too: use the default
fireEvent.error(original)
getByImageSrc(view.container, DEFAULT_RESOURCE_IMG)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ const MitxOnlineResourceCard: React.FC<MitxOnlineResourceCardProps> = (
label,
} = props

const { src: imageSrc, onError: onImageError } = useImageWithFallback(
const {
src: imageSrc,
unoptimized: imageUnoptimized,
onError: onImageError,
} = useImageWithFallback(
props.resource?.page?.feature_image_src,
DEFAULT_RESOURCE_IMG,
)
Expand Down Expand Up @@ -180,6 +184,7 @@ const MitxOnlineResourceCard: React.FC<MitxOnlineResourceCardProps> = (
imageSrc={imageSrc}
imageAlt=""
onImageError={onImageError}
imageUnoptimized={imageUnoptimized}
title={data.title}
resourceType={data.displayType}
resourcePrice={data.resourcePrice}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PostHogEvents } from "@/common/constants"
import type { ResourceInfo } from "./ProductPageTemplate"
import { PlatformEnum } from "api"
import { DEFAULT_RESOURCE_IMG } from "ol-utilities"
import { getAllByImageSrc } from "ol-test-utilities"
import { getAllByImageSrc, queryAllByImageSrc } from "ol-test-utilities"

jest.mock("posthog-js/react", () => ({
...jest.requireActual("posthog-js/react"),
Expand Down Expand Up @@ -74,7 +74,7 @@ const renderProductPageTemplate = (
}

describe("ProductPageTemplate image error fallback", () => {
it("falls back to DEFAULT_RESOURCE_IMG when imageSrc returns 404", () => {
it("falls back to the original image, then DEFAULT_RESOURCE_IMG, when imageSrc fails", () => {
setMockResponse.get(urls.userMe.get(), { is_authenticated: false })
const { view } = renderWithProviders(
<ProductPageTemplate
Expand All @@ -90,9 +90,14 @@ describe("ProductPageTemplate image error fallback", () => {
</ProductPageTemplate>,
)

getAllByImageSrc(view.container, "https://example.com/image.jpg").forEach(
(img) => fireEvent.error(img),
)
const src = "https://example.com/image.jpg"
const raw = { nextJsOriginalSrc: false }
expect(queryAllByImageSrc(view.container, src, raw)).toHaveLength(0)
// Optimized image fails: retry the original, loaded directly
getAllByImageSrc(view.container, src).forEach((img) => fireEvent.error(img))
const originals = getAllByImageSrc(view.container, src, raw)
// Original fails too: use the default
originals.forEach((img) => fireEvent.error(img))
expect(
getAllByImageSrc(view.container, DEFAULT_RESOURCE_IMG).length,
).toBeGreaterThan(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,11 @@ const SidebarMedia: React.FC<{
title: string
priority?: boolean
}> = ({ videoUrl, imageSrc, title, priority }) => {
const { src: resolvedSrc, onError } = useImageWithFallback(
imageSrc,
DEFAULT_RESOURCE_IMG,
)
const {
src: resolvedSrc,
unoptimized,
onError,
} = useImageWithFallback(imageSrc, DEFAULT_RESOURCE_IMG)

if (videoUrl) {
const embedUrl = convertToEmbedUrl(videoUrl)
Expand All @@ -268,6 +269,7 @@ const SidebarMedia: React.FC<{
height={306}
src={resolvedSrc}
alt=""
unoptimized={unoptimized}
onError={onError}
/>
)
Expand Down
Loading
Loading