Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimise Library page load time when channels have large Thumbnail #12530

Merged

Conversation

thesujai
Copy link
Contributor

@thesujai thesujai commented Aug 2, 2024

Summary

As mentioned in the issue this PR does the following changes:

  1. Defines a new API endpoint to get the channel_thumbnail as a img file
  2. Changes the Current internal ChannelMetaData endpoint to return a URL to thumbnail instead directly returning a base64 encoded URL. This will cause async loading for thumbnails in the frontend

ScreenCast
Screencast from 02-08-24 01:44:04 PM IST.webm

References

Fixes #12502

Reviewer guidance

Open the library page and observe the channels load immediately, but the corresponding thumbnails loads asynchronously


Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical user journeys are covered by Gherkin stories
  • Critical and brittle code paths are covered by unit tests

PR process

  • PR has the correct target branch and milestone
  • PR has 'needs review' or 'work-in-progress' label
  • If PR is ready for review, a reviewer has been added. (Don't use 'Assignees')
  • If this is an important user-facing change, PR or related issue has a 'changelog' label
  • If this includes an internal dependency change, a link to the diff is provided

Reviewer checklist

  • Automated test coverage is satisfactory
  • PR is fully functional
  • PR has been tested for accessibility regressions
  • External dependency files were updated if necessary (yarn and pip)
  • Documentation is updated
  • Contributor is in AUTHORS.md

@github-actions github-actions bot added DEV: backend Python, databases, networking, filesystem... APP: Learn Re: Learn App (content, quizzes, lessons, etc.) DEV: frontend labels Aug 2, 2024
@@ -291,6 +294,14 @@ def filter_available(self, queryset, name, value):
return queryset.filter(root__available=value)


class ChannelThumbnailView(View):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For writing unit test for this is there any existing setUp method that will set up a channel metadata model?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look in the test_content_app.py file in the content app, you should see some examples.

computed: {
thumbnailBackground() {
return {
backgroundColor: this.$themeTokens.surface,
backgroundImage: this.thumbnail ? `url('${this.thumbnail}')` : '',
backgroundImage: this.resolvedThumbnail ? `url('${this.resolvedThumbnail}')` : '',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do a high-level fix here, where we just modify the channels on the library page.
Now it is affecting all the cards on the library page. I don't know where to make changes for that.

@MisRob MisRob requested review from jredrejo and rtibbles August 2, 2024 09:09
@rtibbles rtibbles added this to the Kolibri 0.17: Planned Patch 1 milestone Aug 5, 2024
Copy link
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work without any frontend changes, so let's aim for that.

Changes to the channel metadata endpoint can be simplified, and avoid code duplication.

I suspect that the reason the frontend changes were needed is because the backend endpoint is not returning something that the browser interprets as an image file.

for item in items:
item["included_languages"] = included_languages.get(item["id"], [])
item["last_published"] = item["last_updated"]
item["thumbnail"] = reverse(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might be doable using the field_map class property which allows setting to a callable, that would allow the rest of the code not to be copy pasted here.

values = tuple(
field for field in BaseChannelMetadataMixin.values if field != "thumbnail"
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you would add here:

field_map = {
    "thumbnail": _create_channel_thumbnail_url,
}

field_map.update(BaseChannelMetadataMixin.field_map)

Then in the module scope you can define this function:

def _create_channel_thumbnail_url(item):
    return reverse("kolibri:core:channel-thumbnail", args=[item["id"]])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this works, can you share django/python docs covering field_map?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a Django thing - so the documentation for it is in this code comment: https://github.com/learningequality/kolibri/blob/develop/kolibri/core/api.py#L139

@@ -19,6 +19,7 @@
<script>

import ContentIcon from 'kolibri.coreVue.components.ContentIcon';
import { ChannelResource } from 'kolibri.resources';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the ChannelThumbnail endpoint is working as intended, it should be returning a valid file if fetched - so you should just be able to use the URL directly as a file URL.

If this is not working, we need to make some tweaks, but when it is working correctly, this should require no frontend changes at all.

@@ -291,6 +294,14 @@ def filter_available(self, queryset, name, value):
return queryset.filter(root__available=value)


class ChannelThumbnailView(View):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look in the test_content_app.py file in the content app, you should see some examples.

def get(self, request, channel_id):
channel = get_object_or_404(models.ChannelMetadata, id=channel_id)
thumbnail = channel.thumbnail
response = FileResponse(thumbnail)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not confident this will be sufficient - the file is base64 encoded, which has a prefix with the mimetype. I think it is likely you would need to use the base64 decode method before serving this as the contents of a file: https://docs.python.org/3/library/base64.html#base64.b64decode

@thesujai
Copy link
Contributor Author

thesujai commented Aug 5, 2024

@rtibbles can you take another look?

Copy link
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of considerations, and a typo.

Have not yet manually tested!

kolibri/core/content/test/test_content_app.py Outdated Show resolved Hide resolved
kolibri/core/content/api.py Outdated Show resolved Hide resolved
kolibri/core/content/api.py Outdated Show resolved Hide resolved
@thesujai
Copy link
Contributor Author

thesujai commented Aug 5, 2024

I made the changes.
You can do the manual testing.
Now I can observe that when the library page loads, individual HTTP requests are made to get the thumbnail.
The Other libraries section which I suppose makes requests to the public endpoint is not affected by this change, as I see a base64 encoded thumbnail returned in the browser network tab. That is the desired behavior for now.

Copy link
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, after manual QA, this will be good to merge (after we've released 0.17.0).

@rtibbles rtibbles dismissed their stale review August 5, 2024 19:58

Comments have all been addressed.

@radinamatic
Copy link
Member

cc @pcenov

@pcenov
Copy link
Member

pcenov commented Aug 8, 2024

@radinamatic - no issues observed while manually testing, implemented as specified. Good to go!

Copy link
Member

@rtibbles rtibbles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes look good, manual QA checks out, this is ready to go!

@rtibbles rtibbles merged commit 0b24c60 into learningequality:release-v0.17.x Aug 8, 2024
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
APP: Learn Re: Learn App (content, quizzes, lessons, etc.) DEV: backend Python, databases, networking, filesystem... DEV: frontend
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants