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

asadiqbal08/ Django Version bump #2343

Merged
merged 4 commits into from
Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion authentication/middleware_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_process_exception_no_strategy(rf, settings):
def test_process_exception(rf, settings):
"""Tests that a process_exception handles auth exceptions correctly"""
settings.DEBUG = False
msg = "error message"
msg = "This account is already in use."
request = rf.get(reverse("social:complete", args=("email",)))
# social_django depends on request.sesssion, so use the middleware to set that
SessionMiddleware().process_request(request)
Expand Down
34 changes: 34 additions & 0 deletions b2b_ecommerce/migrations/0009_jsonField_from_django_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 3.2.11 on 2022-02-22 12:06

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [("b2b_ecommerce", "0008_b2b_order_program_run")]

operations = [
migrations.AlterField(
model_name="b2bcouponaudit",
name="data_after",
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name="b2bcouponaudit",
name="data_before",
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name="b2borderaudit",
name="data_after",
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name="b2borderaudit",
name="data_before",
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name="b2breceipt", name="data", field=models.JSONField()
),
]
3 changes: 1 addition & 2 deletions b2b_ecommerce/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import uuid

from django.conf import settings
from django.contrib.postgres.fields import JSONField
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models import Q
Expand Down Expand Up @@ -222,7 +221,7 @@ class B2BReceipt(TimestampedModel):
"""

order = models.ForeignKey(B2BOrder, null=True, on_delete=models.PROTECT)
data = JSONField()
data = models.JSONField()

def __str__(self):
"""Description of B2BReceipt"""
Expand Down
2 changes: 1 addition & 1 deletion cms/embeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, providers=None, options=None):

super().__init__(providers=providers, options=options)

def find_embed(self, url, max_width=None):
def find_embed(self, url, max_width=None): # pylint: disable=arguments-differ
embed = super().find_embed(url, max_width)
embed_tag = BeautifulSoup(embed["html"], "html.parser")
player_iframe = embed_tag.find("iframe")
Expand Down
235 changes: 4 additions & 231 deletions cms/migrations/0051_new_page_data_migrations.py
Original file line number Diff line number Diff line change
@@ -1,239 +1,10 @@
# Generated by Django 2.2.20 on 2021-04-28 16:28

"""
Data migration to do the following:

1) Ensure the correct state for course/program index pages and correct depth for course/program detail pages
2) Ensure that a catalog page exists in the right place
3) Ensure that a certificate index page exists in the right place

NOTE: Data migrations are liable to fail if the Wagtail Page model (or potentially other Wagtail models) are changed
from version to version. In those cases, we can set the existing data migration(s) to be a no-op in both directions,
then create a new data migration with the same contents, and add the relevant Wagtail migration as a dependency.
Data migrations moved to cms/migrations/0052_new_page_data_migrations.py in response to Page model change
"""

import datetime
import json

import pytz
from django.db import migrations
from wagtail.core.models import Page, PageRevision

COURSE_INDEX_PAGE_PROPERTIES = dict(title="Courses")
PROGRAM_INDEX_PAGE_PROPERTIES = dict(title="Programs")
CERTIFICATE_INDEX_SLUG = "certificate"


def now_in_utc():
"""
Get the current time in UTC

Returns:
datetime.datetime: A datetime object for the current time
"""
return datetime.datetime.now(tz=pytz.UTC)


def delete_wagtail_pages(specific_page_cls, filter_dict=None):
"""
Completely deletes Wagtail CMS pages that match a filter. Wagtail overrides standard delete functionality,
making it difficult to actually delete Page objects and get information about what was deleted.
"""
page_ids_to_delete = specific_page_cls.objects.values_list("id", flat=True)
if filter_dict:
page_ids_to_delete = page_ids_to_delete.filter(**filter_dict)
num_pages = len(page_ids_to_delete)
base_pages_qset = Page.objects.filter(id__in=page_ids_to_delete)
if not base_pages_qset.exists():
return 0, {}
base_pages_qset.delete()
return (
num_pages,
{specific_page_cls._meta.label: num_pages}, # pylint: disable=protected-access
)


def get_home_page(apps):
"""
Importing the Site model from the registry means if we access the root page from this
model we will get an instance of the Page with only the basic model methods so we simply extract
the ID of the page and hand it to the Page model imported directly.
"""
Site = apps.get_model("wagtailcore", "Site")
site = Site.objects.filter(is_default_site=True).first()
if not site:
raise Exception(
"A default site is not set up. Please setup a default site before running this migration"
)
if not site.root_page:
raise Exception(
"No root (home) page set up. Please setup a root (home) page for the default site before running this migration"
)
return Page.objects.get(id=site.root_page.id)


def create_index_pages_and_nest_detail(apps, schema_editor):
"""
Create index pages for courses and programs and move the respective
course and program pages under these index pages.
"""
from cms.models import CourseIndexPage, ProgramIndexPage

CoursePage = apps.get_model("cms", "CoursePage")
ProgramPage = apps.get_model("cms", "ProgramPage")

# Home page
home_page = get_home_page(apps)

course_index = CourseIndexPage.objects.first()
if not course_index:
page_obj = CourseIndexPage(**COURSE_INDEX_PAGE_PROPERTIES)
course_index = home_page.add_child(instance=page_obj)
program_index = ProgramIndexPage.objects.first()
if not program_index:
page_obj = ProgramIndexPage(**PROGRAM_INDEX_PAGE_PROPERTIES)
program_index = home_page.add_child(instance=page_obj)
# Move course/program detail pages to be children of the course/program index pages
for page_id in CoursePage.objects.values_list("id", flat=True):
page = Page.objects.get(id=page_id)
page.move(course_index, "last-child")
for page_id in ProgramPage.objects.values_list("id", flat=True):
page = Page.objects.get(id=page_id)
page.move(program_index, "last-child")


def unnest_detail_and_delete_index_pages(apps, schema_editor):
"""
Move course and program pages under the home page and remove index pages.
"""
CourseIndexPage = apps.get_model("cms", "CourseIndexPage")
ProgramIndexPage = apps.get_model("cms", "ProgramIndexPage")
CoursePage = apps.get_model("cms", "CoursePage")
ProgramPage = apps.get_model("cms", "ProgramPage")

# Move course/program detail pages to be children of the home page
home_page = get_home_page(apps)
top_level_child_ids = [child.id for child in home_page.get_children()]
for page_id in CoursePage.objects.values_list("id", flat=True):
if page_id not in top_level_child_ids:
page = Page.objects.get(id=page_id)
page.move(home_page, "last-child")
for page_id in ProgramPage.objects.values_list("id", flat=True):
if page_id not in top_level_child_ids:
page = Page.objects.get(id=page_id)
page.move(home_page, "last-child")
# Remove the course/program index pages
delete_wagtail_pages(ProgramIndexPage)
delete_wagtail_pages(CourseIndexPage)


def create_catalog_page(apps, schema_editor):
"""
Create a catalog page under the home page
"""
CatalogPage = apps.get_model("cms", "CatalogPage")
ContentType = apps.get_model("contenttypes.ContentType")
catalog_content_type, _ = ContentType.objects.get_or_create(
app_label="cms", model="catalogpage"
)
home_page = get_home_page(apps)

catalog = CatalogPage.objects.first()
if not catalog:
catalog_page_content = dict(
title="Courseware Catalog",
content_type_id=catalog_content_type.id,
slug="catalog",
locale_id=home_page.get_default_locale().id,
)
catalog_page_obj = CatalogPage(**catalog_page_content)
home_page.add_child(instance=catalog_page_obj)
# NOTE: This block of code creates page revision and publishes it. There may be an easier way to do this.
content_json = json.dumps(dict(**catalog_page_content, pk=catalog_page_obj.id))
revision = PageRevision.objects.create(
page_id=catalog_page_obj.id,
submitted_for_moderation=False,
created_at=now_in_utc(),
content_json=content_json,
)
revision.publish()


def remove_catalog_page(apps, schema_editor):
"""
Remove the catalog page
"""
ContentType = apps.get_model("contenttypes.ContentType")
catalog_content_type, _ = ContentType.objects.get_or_create(
app_label="cms", model="catalogpage"
)
catalog = Page.objects.get(content_type_id=catalog_content_type.id)
if catalog:
catalog.delete()


def create_certificate_index_page(apps, schema_editor):
"""
Create a certificate index page under the home page
"""
CertificateIndexPage = apps.get_model("cms", "CertificateIndexPage")
ContentType = apps.get_model("contenttypes.ContentType")
index_content_type, _ = ContentType.objects.get_or_create(
app_label="cms", model="certificateindexpage"
)
home_page = get_home_page(apps)

index_page = CertificateIndexPage.objects.first()

if not index_page:
index_page_content = dict(
title="Certificate Index Page",
content_type_id=index_content_type.id,
slug=CERTIFICATE_INDEX_SLUG,
locale_id=home_page.get_default_locale().id,
)
index_page_obj = CertificateIndexPage(**index_page_content)
home_page.add_child(instance=index_page_obj)
# NOTE: This block of code creates page revision and publishes it. There may be an easier way to do this.
content_json = json.dumps(dict(**index_page_content, pk=index_page_obj.id))
revision = PageRevision.objects.create(
page_id=index_page_obj.id,
submitted_for_moderation=False,
created_at=now_in_utc(),
content_json=content_json,
)
revision.publish()


def remove_certificate_index_page(apps, schema_editor):
"""
Remove the certificate index page
"""
ContentType = apps.get_model("contenttypes.ContentType")
index_content_type, _ = ContentType.objects.get_or_create(
app_label="cms", model="certificateindexpage"
)
index_page = Page.objects.get(content_type_id=index_content_type.id)
if index_page:
index_page.delete()


def migrate_data(apps, schema_editor):
"""
Apply all data migrations in order
"""
create_index_pages_and_nest_detail(apps, schema_editor)
create_catalog_page(apps, schema_editor)
create_certificate_index_page(apps, schema_editor)


def reverse_migrate_data(apps, schema_editor):
"""
Reverse all data migrations in the opposite order from which they were applied
"""
remove_certificate_index_page(apps, schema_editor)
remove_catalog_page(apps, schema_editor)
unnest_detail_and_delete_index_pages(apps, schema_editor)


class Migration(migrations.Migration):
Expand All @@ -242,4 +13,6 @@ class Migration(migrations.Migration):
("cms", "0050_news_and_events_page"),
]

operations = [migrations.RunPython(migrate_data, reverse_migrate_data)]
operations = [
migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop)
]
Loading