-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move the metadata retrieval to an api function, add a parser for read…
…able ids, fall back to initial data - The metadata retrieval is now in an api function so it's easier to test. - Added a parser for readable ID - skus are generally readable IDs, but if we have a sku that's for a specific run, the learning_resources API won't know what to do with it. - If the data returned from the API doesn't contain data, or doesn't contain _some_ data, fall back to the data that was in the product already. - Added tests for API function - Fixed some names (we're not just pulling image data now)
- Loading branch information
Showing
5 changed files
with
152 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
"""API functions for system metadata.""" | ||
|
||
import logging | ||
|
||
import requests | ||
from django.conf import settings | ||
|
||
from system_meta.models import Product | ||
from unified_ecommerce.utils import parse_readable_id | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def update_product_metadata(product_id: int) -> None: | ||
"""Get product metadata from the Learn API.""" | ||
|
||
try: | ||
product = Product.objects.get(id=product_id) | ||
resource, run = parse_readable_id(product.sku) | ||
response = requests.get( | ||
f"{settings.MITOL_LEARN_API_URL}learning_resources/", | ||
params={"platform": product.system.slug, "readable_id": resource}, | ||
timeout=10, | ||
) | ||
response.raise_for_status() | ||
results_data = response.json() | ||
|
||
if results_data.get("count", 0) == 0: | ||
log.warning("No Learn results found for product %s", product) | ||
return | ||
|
||
course_data = results_data.get("results")[0] | ||
|
||
image_data = course_data.get("image") | ||
product.image_metadata = ( | ||
{ | ||
"image_url": image_data.get("url"), | ||
"alt_text": image_data.get("alt"), | ||
"description": image_data.get("description"), | ||
} | ||
if image_data | ||
else product.image_metadata | ||
) | ||
|
||
product.name = course_data.get("title", product.name) | ||
product.description = course_data.get("description", product.description) | ||
|
||
# If there are runs, we'll overwrite this with the run's price later. | ||
product_prices = course_data.get("prices", []) | ||
product_prices.sort() | ||
product.price = product_prices.pop() if len(product_prices) else product.price | ||
|
||
runs = course_data.get("runs") | ||
if runs: | ||
run = next((r for r in runs if r.get("run_id") == product.sku), None) | ||
if run: | ||
product_prices = run.get("prices", []) | ||
product_prices.sort() | ||
product.price = ( | ||
product_prices.pop() if len(product_prices) else product.price | ||
) | ||
|
||
product.save() | ||
except requests.RequestException: | ||
log.exception("Failed to update metadata for product %s", product.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
"""Tests for the system_meta APIs.""" | ||
|
||
import pytest | ||
|
||
from system_meta.api import update_product_metadata | ||
from system_meta.factories import ProductFactory | ||
|
||
pytestmark = pytest.mark.django_db | ||
|
||
|
||
def test_retrieve_product_metadata(mocker): | ||
"""Test that the retrieve_product_metadata function works.""" | ||
|
||
mocked_requests = mocker.patch("requests.get") | ||
mocked_requests.return_value.json.return_value = { | ||
"results": [ | ||
{ | ||
"image": { | ||
"id": 12345, | ||
"url": "https://example.com/image.jpg", | ||
"alt": "Example image", | ||
"description": "Example description", | ||
}, | ||
"title": "Example title", | ||
"description": "Example description", | ||
"prices": [100, 200], | ||
"readable_id": "course-v1:MITx+12.345x", | ||
"runs": [ | ||
{ | ||
"run_id": "123", | ||
"prices": [150, 250], | ||
"readable_id": "course-v1:MITx+12.345x+2T2099", | ||
} | ||
], | ||
} | ||
] | ||
} | ||
|
||
product = ProductFactory.create( | ||
sku="course-v1:MITx+12.345x+2T2099", | ||
price=50, | ||
description="This is the wrong description.", | ||
) | ||
update_product_metadata(product.id) | ||
product.refresh_from_db() | ||
assert product.image_metadata is not None | ||
assert product.name == "Example title" | ||
assert product.description == "Example description" | ||
# This has a run price, so we should have that, and it should be the highest price. | ||
assert product.price == 250 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,33 @@ | ||
"""Tasks for the system_meta app.""" | ||
|
||
import logging | ||
from typing import Optional | ||
|
||
import requests | ||
from celery import shared_task | ||
from django.conf import settings | ||
|
||
|
||
@shared_task | ||
def update_products(product_id: Optional[int] = None): | ||
""" | ||
Update all product's image metadata. If product_id is provided, only update the | ||
product with that ID. Otherwise, update all products. | ||
Update product metadata from the Learn API. | ||
Updates all products if a product_id is not provided. Pulls the image metadata, | ||
name, and description from the Learn API. If the product has a run ID, it also | ||
pulls the price from the specific run; otherwise, pulls the price from the | ||
resource. | ||
""" | ||
from .models import Product | ||
from system_meta.api import update_product_metadata | ||
from system_meta.models import Product | ||
|
||
log = logging.getLogger(__name__) | ||
if product_id: | ||
products = Product.objects.filter(id=product_id) | ||
else: | ||
products = Product.objects.all() | ||
|
||
for product in products: | ||
try: | ||
response = requests.get( | ||
f"{settings.MITOL_LEARN_API_URL}learning_resources/", | ||
params={"platform": product.system.slug, "readable_id": product.sku}, | ||
timeout=10, | ||
) | ||
response.raise_for_status() | ||
results_data = response.json() | ||
course_data = results_data.get("results")[0] | ||
image_data = course_data.get("image") | ||
product.image_metadata = { | ||
"image_url": image_data.get("url"), | ||
"alt_text": image_data.get("alt"), | ||
"description": image_data.get("description"), | ||
} | ||
product.save() | ||
update_product_metadata(product.id) | ||
except requests.RequestException: | ||
log.exception("Failed to retrieve image data for product %s", product.id) | ||
log.exception("Failed to update metdata for product %s", product.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters