Skip to content

Release 0.180.0#2932

Merged
odlbot merged 9 commits into
releasefrom
release-candidate
Mar 19, 2026
Merged

Release 0.180.0#2932
odlbot merged 9 commits into
releasefrom
release-candidate

Conversation

@odlbot
Copy link
Copy Markdown
Contributor

@odlbot odlbot commented Mar 13, 2026

pt2302

Umar Hassan

zawan-ila

Tobias Macey

  • feat: Add granian for managing the application runtime (b866b637)

Mike Davidson

renovate[bot]

renovate Bot and others added 9 commits March 3, 2026 19:10
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Added a GIT_REF argument to create the hash.txt file.

* Add quotes around
* Update test site contentwith PK12 values

* Remove dangling primary key reference
* feat: changed course-lists referencing to url_path

* chore: fixed circular import error

* chore: addressed feedback and reduced complexity to meet pre-commit checks
* Upgrade to Django 5.2.12

* Update settings test

* Update Header to match Django 5 CSRF requirement

* Always include CSRF cookie
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on upgrading dependencies, enhancing security, adding new features, and improving documentation. The Django upgrade and Granian integration represent significant improvements to the project's foundation, while the documentation update provides valuable guidance for managing course URLs.

Highlights

  • Django Upgrade: The project has been upgraded to Django 5.2.12, bringing in the latest features, security patches, and performance improvements.
  • Granian Integration: Granian, a Python ASGI server, has been added to manage the application runtime, potentially improving performance and concurrency.
  • Logout View Update: The logout view now requires a POST request with CSRF protection, aligning with Django 5.x+ standards for enhanced security.
  • Course URL Change Documentation: Documentation has been added outlining the steps required to change the URL of a published course site, a process that requires direct DB edits and manual steps.
Changelog
  • Dockerfile
    • Added a GIT_REF argument to create the hash.txt file.
  • RELEASE.rst
    • Updated release notes for version 0.180.0, including a summary of changes.
  • content_sync/serializers_test.py
    • Updated settings to use STORAGES dictionary for file storage configuration in tests.
  • docs/changing-a-published-course-url.md
    • Added documentation outlining the steps for changing a published course site URL.
  • main/settings.py
    • Updated the project version to 0.180.0.
  • main/settings_test.py
    • Modified test settings to check for storage backend within the STORAGES dictionary.
  • main/views.py
    • Added ensure_csrf_cookie decorator to the index view for CSRF protection.
  • main/views_test.py
    • Added a test case to ensure the logout view requires a POST request with CSRF protection.
  • pyproject.toml
    • Upgraded Django dependency to version 5.2.12.
    • Added granian as a dependency.
  • static/js/components/Header.test.tsx
    • Updated test to simulate logout via form submission.
  • static/js/components/Header.tsx
    • Modified logout link to submit a form with CSRF token for Django 5.x+ compatibility.
  • static/js/lib/urls.ts
    • Added trailing slash to logoutUrl.
  • test_site_fixtures/test_website_content.json
    • Updated test website content fixtures with new data and metadata fields.
  • test_site_fixtures/test_websites.json
    • Updated test websites fixtures with new build dates.
  • uv.lock
    • Updated dependency versions in uv.lock file.
  • websites/constants.py
    • Added METADATA_FIELD_FEATURED_COURSES to the list of metadata fields.
  • websites/management/commands/backpopulate_referencing_content.py
    • Refactored command to improve performance and accuracy of backpopulating referencing content.
    • Batched text_id lookups to avoid N+1 queries.
    • Resolved course-list url_path references.
  • websites/management/commands/backpopulate_referencing_content_test.py
    • Updated tests for backpopulate_referencing_content command to reflect changes in logic.
    • Removed mocks for compile_referencing_content.
  • websites/signals.py
    • Removed populate_course_list_text_ids_on_save signal.
    • Updated imports.
  • websites/utils.py
    • Removed populate_course_list_text_ids function.
    • Added _resolve_course_list_referenced_content_ids function.
    • Updated compile_referencing_content to handle course lists and featured courses.
    • Updated imports.
  • websites/utils_test.py
    • Updated tests for website utils to reflect changes in logic.
    • Removed tests for populate_course_list_text_ids.
    • Added tests for resolve_referenced_content_ids.
  • websites/views.py
    • Updated imports.
    • Used resolve_referenced_content_ids instead of compile_referencing_content.
Activity
  • Dependency upgrades: Django, pypdf, and others
  • Feature: Granian integration for managing the application runtime
  • Documentation: Added steps for changing the URL of a published course site
  • Security: Logout view now requires POST requests with CSRF protection
  • Refactor: Improved referencing content backpopulation command
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a release that includes several dependency upgrades, most notably an upgrade to Django 5. It also introduces a new feature for tracking content references and adds documentation for changing a published course site URL.

My review focuses on the new content reference tracking implementation. I've identified a couple of performance issues related to N+1 queries when processing course lists, both in a new management command and in a data migration. These could lead to slow performance, especially for the migration, and I've left comments with suggestions for optimization.

Comment on lines 19 to 24
for content in WebsiteContent.objects.iterator(chunk_size=1000):
if references := compile_referencing_content(content):
referenced_objects = WebsiteContent.objects.filter(text_id__in=references)
if referenced_content_ids := resolve_referenced_content_ids(content):
referenced_objects = WebsiteContent.objects.filter(
id__in=referenced_content_ids
)
content.referenced_by.set(referenced_objects)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

This data migration iterates over all WebsiteContent objects and calls resolve_referenced_content_ids for each one. This function has a performance issue: for course-list content, it executes a separate database query for each course URL to resolve it to a sitemetadata object. This will result in a large number of queries (N+1 problem) and could make the migration very slow or even cause it to time out, especially in a production environment with many course lists.

To improve performance, this migration should process content in batches and batch the database queries, similar to the logic in the backpopulate_referencing_content management command. The logic for resolving course list URLs should be optimized to use a single bulk query for all url_paths in a batch.

Comment on lines +178 to +191
def _merge_course_list_ids(
self, content_batch, course_list_ids, content_references
):
"""Merge per-item course-list url_path resolutions into content_references."""
if not course_list_ids:
return
course_list_map = {c.id: c for c in content_batch if c.id in course_list_ids}
for content_id, content in course_list_map.items():
extra_ids = resolve_referenced_content_ids(content)
if extra_ids:
content_references[content_id] = (
content_references.get(content_id, set()) | extra_ids
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This function introduces a performance issue by calling resolve_referenced_content_ids in a loop. The underlying implementation of resolve_referenced_content_ids for course lists performs database queries for each course URL, leading to an N+1 query problem.

To optimize this, you can aggregate all course url_paths from the content_batch, perform a single bulk query to fetch the corresponding WebsiteContent IDs for the sitemetadata, and then use this mapping to update the references. This will significantly reduce the number of database queries.

    def _merge_course_list_ids(
        self, content_batch, course_list_ids, content_references
    ):
        """Merge per-item course-list url_path resolutions into content_references."""
        if not course_list_ids:
            return
        course_list_map = {c.id: c for c in content_batch if c.id in course_list_ids}

        all_url_paths = {
            course_entry.get("id").strip().strip("/")
            for content in course_list_map.values()
            for course_entry in content.metadata.get(
                constants.METADATA_FIELD_COURSE_LIST_COURSES, []
            )
            if isinstance(course_entry, dict)
            and isinstance(course_entry.get("id"), str)
        }

        if not all_url_paths:
            return

        sitemetadata_map = {
            wc.website.url_path: wc.id
            for wc in WebsiteContent.objects.filter(
                website__url_path__in=all_url_paths,
                type=constants.CONTENT_TYPE_METADATA,
            ).select_related("website")
        }

        for content_id, content in course_list_map.items():
            extra_ids = {
                sitemetadata_map[course_entry.get("id").strip().strip("/")]
                for course_entry in content.metadata.get(
                    constants.METADATA_FIELD_COURSE_LIST_COURSES, []
                )
                if isinstance(course_entry, dict)
                and isinstance(course_entry.get("id"), str)
                and course_entry.get("id").strip().strip("/") in sitemetadata_map
            }
            if extra_ids:
                content_references[content_id] = (
                    content_references.get(content_id, set()) | extra_ids
                )

@odlbot odlbot merged commit 1ec6d95 into release Mar 19, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants