Skip to content

Commit

Permalink
Merge pull request #3831 from learningequality/hotfixes
Browse files Browse the repository at this point in the history
Patch release v2022.11.22
  • Loading branch information
bjester committed Nov 22, 2022
2 parents d66b065 + 4acc580 commit 85d80eb
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@
[this.channelFilter]: true,
page: this.$route.query.page || 1,
exclude: this.currentChannelId,
ordering: this.channelFilter === 'public' ? 'name' : '-modified',
}).then(page => {
this.pageCount = page.total_pages;
this.channels = page.results;
Expand Down
62 changes: 23 additions & 39 deletions contentcuration/contentcuration/frontend/shared/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,61 +32,45 @@ const client = axios.create({
client.interceptors.response.use(
response => response,
error => {
let message;
let url;
let config;
const url = error.config.url;
let message = error.message;
let status = 0;
if (error.response) {
config = error.response.config;
url = config.url;
status = error.response.status;
message = error.response.statusText;
// Don't send a Sentry report for permissions errors
// Many 404s are in fact also unauthorized requests so
// we should silence those on the front end and try
// to catch legitimate request issues in the backend.
//
// Allow 412 too as that's specific to out of storage checks
if (
error.response.status === 403 ||
error.response.status === 404 ||
error.response.status === 405 ||
error.response.status === 412
) {
if (status === 403 || status === 404 || status === 405 || status === 412) {
return Promise.reject(error);
}

if (error.response.status === 0) {
message = 'Network Error: ' + url;
}

// Put the URL in the main message for timeouts
// so we can see which timeouts are most frequent.
if (error.response.status === 504) {
message = 'Request Timed Out: ' + url;
}
} else if (error.request && error.request.config) {
// Request was sent but no response received
config = error.request.config;
url = config.url;
message = 'Network Error: ' + url;
} else {
message = error.message;
}

const extraData = {
url,
type: config ? config.responseType : null,
data: config ? config.data : null,
status: error.response ? error.response.status : null,
error: message,
response: error.response ? error.response.data : null,
};
message = message ? `${message}: ${url}` : `Network Error: ${url}`;

if (process.env.NODE_ENV !== 'production') {
// In dev build log warnings to console for developer use
console.warn('AJAX Request Error: ' + message); // eslint-disable-line no-console
console.warn('Error data: ' + JSON.stringify(extraData)); // eslint-disable-line no-console
console.warn('Error data: ', error); // eslint-disable-line no-console
} else {
Sentry.captureMessage(message, {
extra: extraData,
Sentry.withScope(function(scope) {
scope.addAttachment({
filename: 'error.json',
data: JSON.stringify(error),
contentType: 'application/json',
});
Sentry.captureException(new Error(message), {
extra: {
Request: {
headers: error.config.headers,
method: error.config.method,
url,
},
},
});
});
}
return Promise.reject(error);
Expand Down
15 changes: 9 additions & 6 deletions contentcuration/contentcuration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1756,12 +1756,15 @@ def mark_complete(self): # noqa C901
if self.kind_id == content_kinds.EXERCISE:
# Check to see if the exercise has at least one assessment item that has:
if not self.assessment_items.filter(
# A non-blank question
~Q(question='')
# Non-blank answers
& ~Q(answers='[]')
# With either an input question or one answer marked as correct
& (Q(type=exercises.INPUT_QUESTION) | Q(answers__iregex=r'"correct":\s*true'))
# Item with non-blank raw data
~Q(raw_data="") | (
# A non-blank question
~Q(question='')
# Non-blank answers
& ~Q(answers='[]')
# With either an input question or one answer marked as correct
& (Q(type=exercises.INPUT_QUESTION) | Q(answers__iregex=r'"correct":\s*true'))
)
).exists():
errors.append("No questions with question text and complete answers")
# Check that it has a mastery model set
Expand Down
9 changes: 9 additions & 0 deletions contentcuration/contentcuration/tests/test_contentnodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,15 @@ def test_create_exercise_valid_assessment_items(self):
new_obj.mark_complete()
self.assertTrue(new_obj.complete)

def test_create_exercise_valid_assessment_items_raw_data(self):
licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True))
channel = testdata.channel()
new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields)
new_obj.save()
AssessmentItem.objects.create(contentnode=new_obj, raw_data="{\"question\": {}}")
new_obj.mark_complete()
self.assertTrue(new_obj.complete)

def test_create_exercise_no_extra_fields(self):
licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True))
channel = testdata.channel()
Expand Down
17 changes: 6 additions & 11 deletions contentcuration/contentcuration/viewsets/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,6 @@ class ChannelViewSet(ValuesViewset):
serializer_class = ChannelSerializer
pagination_class = ChannelListPagination
filterset_class = ChannelFilter
ordering_fields = ["modified", "name"]
ordering = "-modified"

field_map = channel_field_map
values = base_channel_values + ("edit", "view", "unpublished_changes")
Expand All @@ -414,15 +412,6 @@ def get_queryset(self):
queryset = super(ChannelViewSet, self).get_queryset()
user_id = not self.request.user.is_anonymous and self.request.user.id
user_queryset = User.objects.filter(id=user_id)
# Add the last modified node modified value as the channel last modified
channel_main_tree_nodes = ContentNode.objects.filter(
tree_id=OuterRef("main_tree__tree_id")
)
queryset = queryset.annotate(
modified=Subquery(
channel_main_tree_nodes.values("modified").order_by("-modified")[:1]
)
)

return queryset.annotate(
edit=Exists(user_queryset.filter(editable_channels=OuterRef("id"))),
Expand All @@ -445,6 +434,12 @@ def annotate_queryset(self, queryset):
queryset = queryset.annotate(
count=SQCount(non_topic_content_ids, field="content_id"),
)
# Add the last modified node modified value as the channel last modified
queryset = queryset.annotate(
modified=Subquery(
channel_main_tree_nodes.values("modified").order_by("-modified")[:1]
)
)

queryset = queryset.annotate(unpublished_changes=Exists(_unpublished_changes_query(OuterRef("id"))))

Expand Down

0 comments on commit 85d80eb

Please sign in to comment.