diff --git a/contentcuration/automation/migrations/0001_initial.py b/contentcuration/automation/migrations/0001_initial.py index 95f9bad513..27563d71b7 100644 --- a/contentcuration/automation/migrations/0001_initial.py +++ b/contentcuration/automation/migrations/0001_initial.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('request_hash', models.CharField(max_length=32, null=True)), - ('topic_id', models.UUIDField(blank=True, null=True)), + ('topic_id', models.UUIDField()), ('rank', models.IntegerField(default=0, null=True)), ('override_threshold', models.BooleanField(default=False)), ('timestamp', models.DateTimeField(auto_now_add=True)), diff --git a/contentcuration/automation/models.py b/contentcuration/automation/models.py index 0d92dfd977..48d52134e6 100644 --- a/contentcuration/automation/models.py +++ b/contentcuration/automation/models.py @@ -13,7 +13,7 @@ class RecommendationsCache(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) request_hash = models.CharField(max_length=32, null=True) - topic_id = models.UUIDField(null=True, blank=True) + topic_id = models.UUIDField() contentnode = models.ForeignKey( ContentNode, null=True, diff --git a/contentcuration/contentcuration/frontend/RecommendedResourceCard/components/RecommendedResourceCard.vue b/contentcuration/contentcuration/frontend/RecommendedResourceCard/components/RecommendedResourceCard.vue index f510408f03..8408c4bfbc 100644 --- a/contentcuration/contentcuration/frontend/RecommendedResourceCard/components/RecommendedResourceCard.vue +++ b/contentcuration/contentcuration/frontend/RecommendedResourceCard/components/RecommendedResourceCard.vue @@ -78,7 +78,9 @@ computed: { ...mapState('importFromChannels', ['selected']), channelName() { - return this.node.title; + const ancestors = this.node.ancestors || []; + const channel = ancestors.find(ancestor => ancestor.id === this.node.channel_id); + return channel ? channel.title : this.node.title; }, learningActivities() { if (this.node.learning_activities && Object.keys(this.node.learning_activities).length) { diff --git a/contentcuration/contentcuration/tests/viewsets/test_recommendations.py b/contentcuration/contentcuration/tests/viewsets/test_recommendations.py index ef615f69ff..7e92129143 100644 --- a/contentcuration/contentcuration/tests/viewsets/test_recommendations.py +++ b/contentcuration/contentcuration/tests/viewsets/test_recommendations.py @@ -1,3 +1,4 @@ +from automation.utils.appnexus import errors from django.urls import reverse from mock import patch @@ -89,7 +90,7 @@ def test_recommendation_invalid_data_formats(self, mock_load_recommendations): self.client.force_authenticate(user=self.admin_user) error_message = "Invalid input provided." - mock_load_recommendations.side_effect = ValueError(error_message) + mock_load_recommendations.side_effect = errors.InvalidRequest(error_message) response = self.client.post(reverse("recommendations"), data=self.topics, format="json") @@ -103,7 +104,7 @@ def test_recommendation_service_unavailable(self, mock_load_recommendations): self.client.force_authenticate(user=self.admin_user) error_message = "Recommendation service unavailable" - mock_load_recommendations.side_effect = ConnectionError(error_message) + mock_load_recommendations.side_effect = errors.ConnectionError(error_message) response = self.client.post(reverse("recommendations"), data=self.topics, format="json") @@ -117,7 +118,7 @@ def test_recommendation_generic_error(self, mock_load_recommendations): self.client.force_authenticate(user=self.admin_user) error_message = "Unable to load recommendations" - mock_load_recommendations.side_effect = RuntimeError(error_message) + mock_load_recommendations.side_effect = errors.HttpError(error_message) response = self.client.post(reverse("recommendations"), data=self.topics, format="json") diff --git a/contentcuration/contentcuration/viewsets/recommendation.py b/contentcuration/contentcuration/viewsets/recommendation.py index ac469350d7..8b87294ddf 100644 --- a/contentcuration/contentcuration/viewsets/recommendation.py +++ b/contentcuration/contentcuration/viewsets/recommendation.py @@ -2,6 +2,7 @@ from http import HTTPStatus import jsonschema +from automation.utils.appnexus import errors from django.http import HttpResponseServerError from django.http import JsonResponse from le_utils.validators import embed_topics_request @@ -36,14 +37,14 @@ def post(self, request): try: recommendations = self.manager.load_recommendations(request_data, override_threshold) return JsonResponse(data=recommendations, safe=False) - except (ValueError, TypeError) as e: - logger.error("Validation error occurred: %s", str(e), exc_info=True) + except errors.InvalidRequest: return JsonResponse({"error": "Invalid input provided."}, status=HTTPStatus.BAD_REQUEST) - except ConnectionError as e: - logger.error("Connection error occurred: %s", str(e), exc_info=True) + except errors.ConnectionError: return JsonResponse({"error": "Recommendation service unavailable"}, status=HTTPStatus.SERVICE_UNAVAILABLE) - except Exception as e: - logger.error("Unexpected error occurred: %s", str(e), exc_info=True) + except errors.TimeoutError: + return JsonResponse({"error": "Connection to recommendation service timed out"}, + status=HTTPStatus.REQUEST_TIMEOUT) + except errors.HttpError: return HttpResponseServerError("Unable to load recommendations")