Skip to content

Commit

Permalink
feat(emeritus course sync): add image and certificates for external c…
Browse files Browse the repository at this point in the history
…ourses (#3064)

* feat: add emeritus course image and ceus

* test: add tests for the certificate page create/update

* test: add tests for emeritus course image add/update

* fix: save revision when there are changes

* fix: do not publish certificate if saved as draft

* publish external certificates and display CEUs for courseware pages

* test: add test for product page context

* review changes

* review changes

* revert unintentional change

* review changes
  • Loading branch information
asadali145 authored Aug 5, 2024
1 parent a5ee481 commit 912a4e5
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 8 deletions.
15 changes: 15 additions & 0 deletions cms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ def __init__(self, data=None, files=None, parent_page=None, *args, **kwargs):
"readable_id"
].parent_readable_id = parent_page.specific.course.readable_id

def clean(self):
"""
Validates that certificate page signatories are added for internal courseware.
"""
from cms.models import CoursePage, ProgramPage

cleaned_data = super().clean()
parent_page = self.parent_page.specific
if (isinstance(parent_page, (CoursePage, ProgramPage))) and not cleaned_data[
"signatories"
]:
self.add_error("signatories", "Signatories is a required field.")

return cleaned_data


class CoursewareForm(WagtailAdminPageForm):
"""
Expand Down
31 changes: 31 additions & 0 deletions cms/migrations/0071_alter_certificatepage_signatories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2.13 on 2024-07-25 11:29

import wagtail.blocks
import wagtail.fields
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("cms", "0070_alter_coursepage_topics_and_more"),
]

operations = [
migrations.AlterField(
model_name="certificatepage",
name="signatories",
field=wagtail.fields.StreamField(
[
(
"signatory",
wagtail.blocks.PageChooserBlock(
page_type=["cms.SignatoryPage"], required=False
),
)
],
blank=True,
help_text="You can choose upto 5 signatories.",
use_json_field=True,
),
),
]
8 changes: 6 additions & 2 deletions cms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ def get_context(self, request, *args, **kwargs):
"techniques": self.techniques,
"propel_career": self.propel_career,
"news_and_events": self.news_and_events,
"ceus": self.certificate_page.CEUs if self.certificate_page else None,
}

def save(self, clean=True, user=None, log_action=False, **kwargs): # noqa: FBT002
Expand Down Expand Up @@ -2150,12 +2151,15 @@ class PartnerLogoPlacement(models.IntegerChoices):
[
(
"signatory",
PageChooserBlock(required=True, target_model=["cms.SignatoryPage"]),
PageChooserBlock(
required=False, target_model=["cms.SignatoryPage"]
),
)
],
min_num=1,
min_num=0,
max_num=5,
),
blank=True,
help_text="You can choose upto 5 signatories.",
use_json_field=True,
)
Expand Down
118 changes: 118 additions & 0 deletions cms/models_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1968,3 +1968,121 @@ def test_get_child_page_of_type_including_draft():
== learning_outcomes_page
)
assert not LearningOutcomesPage.can_create_at(external_course_page)


@pytest.mark.parametrize("page_factory", [CoursePageFactory, ProgramPageFactory])
def test_certificatepage_no_signatories_internal_courseware(
superuser_client, page_factory
):
"""
Tests that an error is raised when signatories are empty for internal courseware certificates.
"""
home = HomePageFactory.create()
home.save_revision().publish()

page = page_factory.create(parent=home, certificate_page=None)
page.save_revision().publish()

certificate_page = CertificatePageFactory.create(
parent=page,
product_name="product_name",
CEUs="2.8",
partner_logo=None,
)

path = reverse("wagtailadmin_pages:edit", kwargs={"page_id": certificate_page.id})
response = superuser_client.get(path)

data_to_post = querydict_from_html(
response.content.decode(), form_id="page-edit-form"
)
data_to_post["overrides-count"] = 0
data_to_post["signatories-count"] = 0

resp = superuser_client.post(path, data_to_post)
assert resp.status_code == 200
assert resp.context["form"].errors["signatories"] == [
"Signatories is a required field."
]


@pytest.mark.parametrize("page_factory", [CoursePageFactory, ProgramPageFactory])
def test_certificatepage_with_signatories_internal_courseware(
superuser_client, page_factory
):
"""
Tests that certificate page is published when signatories are added for internal courseware.
"""
home = HomePageFactory.create()
home.save_revision().publish()

page = page_factory.create(parent=home, certificate_page=None)
page.save_revision().publish()

signatory = SignatoryPageFactory(
name="Name",
title_1="Title_1",
title_2="Title_2",
organization="Organization",
signature_image__image__title="Image",
)
certificate_page = CertificatePageFactory.create(
parent=page,
product_name="product_name",
CEUs="2.8",
partner_logo=None,
signatories__0__signatory__page=signatory,
)

path = reverse("wagtailadmin_pages:edit", kwargs={"page_id": certificate_page.id})
response = superuser_client.get(path)

data_to_post = querydict_from_html(
response.content.decode(), form_id="page-edit-form"
)
data_to_post["action-publish"] = "action-publish"
data_to_post["overrides-count"] = 0
data_to_post["signatories-count"] = 1
data_to_post["signatories-0-deleted"] = ""
data_to_post["signatories-0-order"] = 0
data_to_post["signatories-0-type"] = "signatory"

resp = superuser_client.post(path, data_to_post)
assert resp.status_code == 302
assert certificate_page.signatories is not None


@pytest.mark.parametrize(
"page_factory", [ExternalCoursePageFactory, ExternalProgramPageFactory]
)
def test_certificatepage_saved_no_signatories_external_courseware(
superuser_client, page_factory
):
"""
Tests that certificate page is saved without signatories for external courseware.
"""
home = HomePageFactory.create()
home.save_revision().publish()

page = page_factory.create(parent=home)
page.save_revision().publish()

certificate_page = CertificatePageFactory.create(
parent=page,
product_name="product_name",
CEUs="2.8",
partner_logo=None,
)

path = reverse("wagtailadmin_pages:edit", kwargs={"page_id": certificate_page.id})
response = superuser_client.get(path)

data_to_post = querydict_from_html(
response.content.decode(), form_id="page-edit-form"
)
data_to_post["overrides-count"] = 0
data_to_post["action-publish"] = "action-publish"
data_to_post["signatories-count"] = 0

resp = superuser_client.post(path, data_to_post)
assert resp.status_code == 302
6 changes: 6 additions & 0 deletions cms/templates/partials/metadata-tiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
<span class="title">FORMAT</span>
<span class="text">{{ page.format }}</span>
</li>
{% if ceus %}
<li>
<span class="title">CEUs</span>
<span class="text">{{ ceus|floatformat }}</span>
</li>
{% endif %}
{% if page.product.current_price %}
<li>
<span class="title">PRICE</span>
Expand Down
46 changes: 46 additions & 0 deletions cms/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
from cms.factories import (
BlogIndexPageFactory,
CatalogPageFactory,
CertificatePageFactory,
CourseIndexPageFactory,
CoursePageFactory,
EnterprisePageFactory,
ExternalCoursePageFactory,
ExternalProgramPageFactory,
HomePageFactory,
ProgramIndexPageFactory,
ProgramPageFactory,
Expand Down Expand Up @@ -616,3 +619,46 @@ def test_enterprise_page_context(client, wagtail_basics):
assert context["hubspot_enterprise_page_form_id"] == settings.HUBSPOT_CONFIG.get(
"HUBSPOT_ENTERPRISE_PAGE_FORM_ID"
)


@pytest.mark.parametrize(
("page_factory", "published_certificate"),
[
(CoursePageFactory, True),
(CoursePageFactory, False),
(ExternalCoursePageFactory, True),
(ExternalCoursePageFactory, False),
(ProgramPageFactory, True),
(ProgramPageFactory, False),
(ExternalProgramPageFactory, True),
(ExternalProgramPageFactory, False),
],
)
def test_product_page_context_has_certificate(
page_factory, published_certificate, client
):
"""
Tests that product page context has certificate.
"""
if page_factory in (CoursePageFactory, ProgramPageFactory):
page = page_factory.create(certificate_page=None)
else:
page = page_factory.create()

page.save_revision().publish()
certificate_page = CertificatePageFactory.create(parent=page, CEUs="12.0")
revision = certificate_page.save_revision()
if published_certificate:
revision.publish()
else:
certificate_page.unpublish()

resp = client.get(page.get_url())
assert resp.status_code == 200
assert "ceus" in resp.context

if published_certificate:
assert resp.context["ceus"] is not None
assert resp.context["ceus"] == "12.0"
else:
assert resp.context["ceus"] is None
20 changes: 20 additions & 0 deletions courses/management/commands/sync_external_course_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@ def handle(self, *args, **options): # noqa: ARG002
f"External Course Codes: {stats['course_pages_updated'] if stats['course_pages_updated'] else None}.\n"
)
)
self.stdout.write(
self.style.SUCCESS(
f"Number of Certificate Pages Created {len(stats['certificates_created'])}."
)
)
self.stdout.write(
self.style.SUCCESS(
f"Course Readable IDs: {stats['certificates_created'] if stats['certificates_created'] else None}.\n"
)
)
self.stdout.write(
self.style.SUCCESS(
f"Number of Certificate Pages Updated {len(stats['certificates_updated'])}."
)
)
self.stdout.write(
self.style.SUCCESS(
f"Course Readable IDs: {stats['certificates_updated'] if stats['certificates_updated'] else None}.\n"
)
)
self.stdout.write(
self.style.SUCCESS(
f"Number of Course Runs Skipped due to bad data {len(stats['course_runs_skipped'])}."
Expand Down
Loading

0 comments on commit 912a4e5

Please sign in to comment.