diff --git a/activitystream/serializers.py b/activitystream/serializers.py
index 31ef207a5c..0d5881cadd 100644
--- a/activitystream/serializers.py
+++ b/activitystream/serializers.py
@@ -30,7 +30,7 @@ def _prep_richtext_for_indexing(self, rich_text_value: str) -> str:
def to_representation(self, obj):
return {
- 'id': ('dit:greatCms:Article:' + str(obj.id) + ':Update'),
+ 'id': 'dit:greatCms:Article:' + str(obj.id) + ':Update',
'type': 'Update',
'published': obj.last_published_at.isoformat('T'),
'object': {
@@ -77,7 +77,7 @@ def _get_article_body_content_for_search(self, obj: ArticlePage) -> str:
def to_representation(self, obj):
return {
- 'id': ('dit:greatCms:Article:' + str(obj.id) + ':Update'),
+ 'id': 'dit:greatCms:Article:' + str(obj.id) + ':Update',
'type': 'Update',
'published': obj.last_published_at.isoformat('T'),
'object': {
@@ -134,7 +134,7 @@ def _get_microsite_body_content_for_search(self, obj: MicrositePage) -> str:
def to_representation(self, obj):
return {
- 'id': ('dit:greatCms:Microsite:' + str(obj.id) + ':Update'),
+ 'id': 'dit:greatCms:Microsite:' + str(obj.id) + ':Update',
'type': 'Update',
'published': obj.last_published_at.isoformat('T'),
'object': {
@@ -144,7 +144,7 @@ def to_representation(self, obj):
'summary': obj.page_teaser,
'content': self._get_microsite_body_content_for_search(obj),
'url': f'https://www.great.gov.uk{obj.get_url()}',
- 'locale_id': obj.locale_id
+ 'locale_id': obj.locale_id,
# 'keywords': ' '.join(obj.tags.all().values_list('name', flat=True)),
},
}
diff --git a/config/wsgi.py b/config/wsgi.py
index 98730e9605..a544518541 100644
--- a/config/wsgi.py
+++ b/config/wsgi.py
@@ -6,6 +6,7 @@
For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""
+
import os
from django.core.wsgi import get_wsgi_application
diff --git a/contact/models.py b/contact/models.py
index 319b9b4889..040dcdc81b 100644
--- a/contact/models.py
+++ b/contact/models.py
@@ -22,44 +22,44 @@ class ContactUsGuidanceSnippet(
# if we need to trace through where content goes)
snippet_slugs.HELP_EXOPP_ALERTS_IRRELEVANT: {
'title': 'Guidance - Daily alerts are not relevant',
- 'page_path': ('/contact/triage/export-opportunities/alerts-not-relevant/'),
+ 'page_path': '/contact/triage/export-opportunities/alerts-not-relevant/',
},
snippet_slugs.HELP_EXOPPS_NO_RESPONSE: {
'title': 'Guidance - Export Opportunity application no response',
- 'page_path': ('/contact/triage/export-opportunities/opportunity-no-response/'),
+ 'page_path': '/contact/triage/export-opportunities/opportunity-no-response/',
},
snippet_slugs.HELP_MISSING_VERIFY_EMAIL: {
'title': 'Guidance - Email verification missing',
- 'page_path': ('/contact/triage/great-account/no-verification-email/'),
+ 'page_path': '/contact/triage/great-account/no-verification-email/',
},
snippet_slugs.HELP_PASSWORD_RESET: {
'title': 'Guidance - Missing password reset link',
- 'page_path': ('/contact/triage/great-account/password-reset/'),
+ 'page_path': '/contact/triage/great-account/password-reset/',
},
snippet_slugs.HELP_COMPANIES_HOUSE_LOGIN: {
'title': 'Guidance - Companies House login not working',
- 'page_path': ('/contact/triage/great-account/companies-house-login/'),
+ 'page_path': '/contact/triage/great-account/companies-house-login/',
},
snippet_slugs.HELP_VERIFICATION_CODE_ENTER: {
'title': 'Guidance - Where to enter letter verification code',
- 'page_path': ('/contact/triage/great-account/verification-letter-code/'),
+ 'page_path': '/contact/triage/great-account/verification-letter-code/',
},
snippet_slugs.HELP_VERIFICATION_CODE_LETTER: {
'title': 'Guidance - Verification letter not delivered',
- 'page_path': ('/contact/triage/great-account/no-verification-letter/'),
+ 'page_path': '/contact/triage/great-account/no-verification-letter/',
},
snippet_slugs.HELP_VERIFICATION_CODE_MISSING: {
'title': 'Guidance - Verification code not delivered',
- 'page_path': ('/contact/triage/great-account/verification-missing/'),
+ 'page_path': '/contact/triage/great-account/verification-missing/',
},
snippet_slugs.HELP_ACCOUNT_COMPANY_NOT_FOUND: {
'title': 'Guidance - Company not found',
- 'page_path': ('/contact/triage/great-account/company-not-found/'),
+ 'page_path': '/contact/triage/great-account/company-not-found/',
},
snippet_slugs.HELP_EXPORTING_TO_UK: {
# NB snippet_slugs.HELP_EXPORTING_TO_UK is NOT bootstrapped via data migration
'title': 'Guidance - Exporting to the UK',
- 'page_path': ('contact/triage/international/exporting-to-the-uk/'),
+ 'page_path': 'contact/triage/international/exporting-to-the-uk/',
},
}
@@ -119,7 +119,7 @@ class ContactSuccessSnippet(
},
snippet_slugs.HELP_FORM_SUCCESS_DSO: {
'title': 'Contact Defence and Security Organisation form success page content',
- 'page_path': ('/contact/defence-and-security-organisation/success/'),
+ 'page_path': '/contact/defence-and-security-organisation/success/',
},
snippet_slugs.HELP_FORM_SUCCESS_EXPORT_ADVICE: {
'title': 'Contact exporting from the UK form success page content',
diff --git a/core/components/static/icons/event-icon.jpeg b/core/components/static/icons/event-icon.jpeg
new file mode 100644
index 0000000000..85cc7e57c7
Binary files /dev/null and b/core/components/static/icons/event-icon.jpeg differ
diff --git a/core/components/static/icons/series-icon.jpeg b/core/components/static/icons/series-icon.jpeg
new file mode 100644
index 0000000000..16a8aad406
Binary files /dev/null and b/core/components/static/icons/series-icon.jpeg differ
diff --git a/core/migrations/0130_ukeacta_detailpage_call_to_action_eventorderable.py b/core/migrations/0130_ukeacta_detailpage_call_to_action_eventorderable.py
new file mode 100644
index 0000000000..a210cf73a7
--- /dev/null
+++ b/core/migrations/0130_ukeacta_detailpage_call_to_action_eventorderable.py
@@ -0,0 +1,99 @@
+# Generated by Django 4.1.13 on 2024-01-12 17:57
+
+import django.db.models.deletion
+import modelcluster.fields
+import wagtail.blocks
+import wagtail.fields
+import wagtail.snippets.blocks
+from django.db import migrations, models
+
+import core.blocks
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('export_academy', '0033_alter_coursepage_metadata_alter_coursepage_reviews'),
+ ('core', '0129_alter_greatmedia_subtitles_en_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='UKEACTA',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ (
+ 'image',
+ wagtail.fields.StreamField(
+ [
+ (
+ 'media',
+ wagtail.blocks.StreamBlock([('image', core.blocks.ImageBlock())], max_num=1, min_num=1),
+ )
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ ('name', models.CharField(help_text='Snippet name', max_length=50)),
+ ],
+ options={
+ 'verbose_name': 'UKEA CTA',
+ 'verbose_name_plural': "UKEA CTA's",
+ },
+ ),
+ migrations.AddField(
+ model_name='detailpage',
+ name='call_to_action',
+ field=wagtail.fields.StreamField(
+ [
+ (
+ 'ukea_article_cta',
+ wagtail.blocks.ListBlock(
+ wagtail.snippets.blocks.SnippetChooserBlock('core.UKEACTA'),
+ icon='link',
+ label='UKEA Call to action',
+ max_num=1,
+ template='learn/includes/article_page_cta.html',
+ ),
+ )
+ ],
+ blank=True,
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ migrations.CreateModel(
+ name='EventOrderable',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
+ (
+ 'event',
+ models.ForeignKey(
+ blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='export_academy.event'
+ ),
+ ),
+ (
+ 'page',
+ modelcluster.fields.ParentalKey(
+ on_delete=django.db.models.deletion.CASCADE, related_name='ukea_cta_links', to='core.ukeacta'
+ ),
+ ),
+ (
+ 'series',
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to='export_academy.coursepage',
+ ),
+ ),
+ ],
+ options={
+ 'ordering': ['sort_order'],
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index 1fb80f81a7..ae3bbc89d9 100644
--- a/core/models.py
+++ b/core/models.py
@@ -723,7 +723,6 @@ def is_lesson_page(self, page):
class LessonPlaceholderPage(Page, mixins.AuthenticatedUserRequired if not settings.FEATURE_DEA_V2 else object):
-
"""Structural page to allow for configuring and representing very simple
to modules (`CuratedListPage`s).
@@ -906,6 +905,24 @@ class Meta:
use_json_field=True,
)
+ call_to_action = StreamField(
+ [
+ (
+ 'ukea_article_cta',
+ blocks.ListBlock(
+ SnippetChooserBlock('core.UKEACTA'),
+ template='learn/includes/article_page_cta.html',
+ label='UKEA Call to action',
+ max_num=1,
+ icon='link',
+ ),
+ )
+ ],
+ use_json_field=True,
+ null=True,
+ blank=True,
+ )
+
def get_steps(self):
topics = CuratedListPage.objects.live()
return [{'text': page.title, 'url': page.url} for page in topics]
@@ -929,6 +946,7 @@ def get_current_module(self):
FieldPanel('objective'),
FieldPanel('body'),
FieldPanel('recap'),
+ FieldPanel('call_to_action'),
]
@cached_classmethod
@@ -1042,6 +1060,56 @@ def get_context(self, request, *args, **kwargs):
return context
+class EventOrderable(Orderable):
+ """
+ This allows us to either series or multiple events
+ """
+
+ page = ParentalKey('core.UKEACTA', related_name='ukea_cta_links')
+ event = models.ForeignKey('export_academy.Event', on_delete=models.SET_NULL, null=True, blank=True)
+ series = models.ForeignKey('export_academy.CoursePage', on_delete=models.SET_NULL, null=True, blank=True)
+
+ panels = [FieldPanel('event'), FieldPanel('series')]
+
+
+@register_snippet
+class UKEACTA(ClusterableModel):
+ image = StreamField(
+ [
+ (
+ 'media',
+ blocks.StreamBlock(
+ [
+ ('image', core_blocks.ImageBlock()),
+ ],
+ min_num=1,
+ max_num=1,
+ ),
+ ),
+ ],
+ use_json_field=True,
+ blank=True,
+ null=True,
+ )
+ name = models.CharField(max_length=50, help_text='Snippet name')
+ panels = [
+ FieldPanel('image'),
+ FieldPanel('name'),
+ MultiFieldPanel(
+ [InlinePanel('ukea_cta_links', label='Link')],
+ heading='Link(s)',
+ icon='link',
+ ),
+ ]
+
+ class Meta:
+ verbose_name = 'UKEA CTA'
+ verbose_name_plural = "UKEA CTA's"
+
+ def __str__(self):
+ return self.name
+
+
@register_snippet
class RelatedContentCTA(models.Model):
type_choices = [
diff --git a/export_academy/helpers.py b/export_academy/helpers.py
index f7c4606ba8..82b9a6f413 100644
--- a/export_academy/helpers.py
+++ b/export_academy/helpers.py
@@ -140,9 +140,11 @@ def get_ics_button(event, on_confirmation):
f'Add to calendar{event.name}',
'value': 'Confirmed',
'type': 'submit',
- 'classname': 'govuk-button ukea-ga-tracking govuk-!-margin-bottom-0'
- if on_confirmation
- else 'govuk-button govuk-button--secondary ukea-ga-tracking govuk-!-margin-bottom-0',
+ 'classname': (
+ 'govuk-button ukea-ga-tracking govuk-!-margin-bottom-0'
+ if on_confirmation
+ else 'govuk-button govuk-button--secondary ukea-ga-tracking govuk-!-margin-bottom-0'
+ ),
}
diff --git a/exportplan/forms.py b/exportplan/forms.py
index a7c858a70a..bfdc35093d 100644
--- a/exportplan/forms.py
+++ b/exportplan/forms.py
@@ -30,7 +30,7 @@ class ExportPlanAdaptingYourProductForm(forms.Form):
'research what the requirements are so your products have the correct labels for your '
'target market.'
),
- 'placeholder': ('Describe alterations'),
+ 'placeholder': 'Describe alterations',
}
),
)
@@ -45,7 +45,7 @@ class ExportPlanAdaptingYourProductForm(forms.Form):
'on the market.You will have to research packaging requirements for your target market to avoid '
'your products becoming damaged, lost or rejected.'
),
- 'placeholder': ('Describe alterations'),
+ 'placeholder': 'Describe alterations',
}
),
)
@@ -60,7 +60,7 @@ class ExportPlanAdaptingYourProductForm(forms.Form):
'shopping trips. You will have to research the size of products sold in this market so '
'you meet customer needs for your target market.'
),
- 'placeholder': ('Describe alterations'),
+ 'placeholder': 'Describe alterations',
}
),
)
@@ -75,39 +75,39 @@ class ExportPlanAdaptingYourProductForm(forms.Form):
'in order to comply with safety regulations in that market. You will have to research '
'standards relevant to your product to make sure they are compliant.'
),
- 'placeholder': ('Describe alterations'),
+ 'placeholder': 'Describe alterations',
}
),
)
translations = forms.CharField(
label='Translations',
required=False,
- widget=Textarea(attrs={'tooltip': ('Translations'), 'placeholder': ('Describe alterations')}),
+ widget=Textarea(attrs={'tooltip': 'Translations', 'placeholder': 'Describe alterations'}),
)
other_changes = forms.CharField(
label='Other changes',
required=False,
- widget=Textarea(attrs={'tooltip': ('Other changes'), 'placeholder': ('Describe alterations')}),
+ widget=Textarea(attrs={'tooltip': 'Other changes', 'placeholder': 'Describe alterations'}),
)
certificate_of_origin = forms.CharField(
label='Certificate of origin',
required=False,
- widget=Textarea(attrs={'tooltip': ('Certificate of origin'), 'placeholder': ('Add notes')}),
+ widget=Textarea(attrs={'tooltip': 'Certificate of origin', 'placeholder': 'Add notes'}),
)
insurance_certificate = forms.CharField(
label='Insurance certificate',
required=False,
- widget=Textarea(attrs={'tooltip': ('Insurance certificate'), 'placeholder': ('Add note')}),
+ widget=Textarea(attrs={'tooltip': 'Insurance certificate', 'placeholder': 'Add note'}),
)
commercial_invoice = forms.CharField(
label='Commercial invoice',
required=False,
- widget=Textarea(attrs={'tooltip': ('Commercial invoice'), 'placeholder': ('Add note')}),
+ widget=Textarea(attrs={'tooltip': 'Commercial invoice', 'placeholder': 'Add note'}),
)
uk_customs_declaration = forms.CharField(
label='UK customs declaration',
required=False,
- widget=Textarea(attrs={'tooltip': ('UK customs declaration'), 'placeholder': ('Add note')}),
+ widget=Textarea(attrs={'tooltip': 'UK customs declaration', 'placeholder': 'Add note'}),
)
@@ -203,7 +203,7 @@ def set_country_specific_text(self, country_name):
average_price = forms.CharField(
required=False,
widget=NumberInput(
- attrs={'placeholder': ('0.00'), 'currency': ('GBP')},
+ attrs={'placeholder': '0.00', 'currency': 'GBP'},
),
)
diff --git a/learn/templates/learn/includes/article_page_cta.html b/learn/templates/learn/includes/article_page_cta.html
index 330ab14d2a..712cc26c4b 100644
--- a/learn/templates/learn/includes/article_page_cta.html
+++ b/learn/templates/learn/includes/article_page_cta.html
@@ -1,4 +1,6 @@
+{% load get_article_cta_attributes from helpers %}
Learn more with free training
- {% include 'components/great/card.html' with heading_class="great-card__link--heading--18" heading_level="h3" tag_container_inner_class="article-page-cta-container-inner-container" container_class="article-page-cta-container-title-container" classes="great-card--cta great-card--cta-with-content govuk-!-margin-bottom-8" title="Join the UK Export Academy" content="Free training with Q&A, helping you learn to sell confidently to overseas customers" content_tag="p" image_src="/static/images/ukea-landing.png" is_svg_image=False url="/export-academy" show_title_link=True tag="Service" tag_icon="/static/icons/hand.svg" %}
-
+ {% get_article_cta_attributes page.call_to_action.0.value.0 as cta_attrs %}
+ {% include 'components/great/card.html' with heading_class="great-card__link--heading--18" heading_level="h3" tag_container_inner_class="article-page-cta-container-inner-container" container_class="article-page-cta-container-title-container" classes="great-card--cta great-card--cta-with-content govuk-!-margin-bottom-8" title=cta_attrs.title content=cta_attrs.description content_tag="p" image_src=cta_attrs.image is_svg_image=False url=cta_attrs.link show_title_link=True tag=cta_attrs.type tag_icon=cta_attrs.icon %}
+
\ No newline at end of file
diff --git a/learn/templatetags/helpers.py b/learn/templatetags/helpers.py
index 5347a0c65b..73e8a9acfb 100644
--- a/learn/templatetags/helpers.py
+++ b/learn/templatetags/helpers.py
@@ -1,7 +1,10 @@
from django import template
+from django.urls import reverse
+from django.utils import timezone
from wagtail.models import Page
-from core.models import RelatedContentCTA
+from core.models import UKEACTA, RelatedContentCTA
+from export_academy.models import Event
register = template.Library()
@@ -18,3 +21,57 @@ def get_cta_attributes(cta: RelatedContentCTA):
result['tag_description'] = dict(RelatedContentCTA.type_choices)[cta.type]
result['tag_icon'] = '/static/icons/hand.svg' if 'service' in cta.type.lower() else '/static/icons/guidance.svg'
return result
+
+
+def get_first_available_event(event_ids: list):
+ first_available_event = None
+ for event in Event.objects.filter(id__in=event_ids).order_by('start_date'):
+ if event.start_date > timezone.now() and event.live and not event.completed:
+ return event
+ return first_available_event
+
+
+@register.simple_tag
+def get_article_cta_attributes(cta: UKEACTA) -> dict:
+ default_data = {
+ 'link': '/export-academy',
+ 'image': '/static/images/ukea-landing.png',
+ 'icon': '/static/icons/hand.svg',
+ 'title': 'Join the UK Export Academy',
+ 'description': 'Free training with Q&A, helping you learn to sell confidently to overseas customers',
+ 'type': 'Service',
+ }
+ if not cta:
+ return default_data
+
+ links = cta.ukea_cta_links.all()
+ series = [
+ {
+ 'title': link.series.title,
+ 'description': link.series.summary,
+ 'icon': '/static/icons/series-icon.jpeg',
+ 'type': 'Series',
+ 'image': cta.image[0].value[0].value.file.url if cta.image else default_data['image'],
+ 'link': reverse('export_academy:course', kwargs={'slug': link.series.slug}),
+ }
+ for link in links
+ if link.series
+ ]
+ # We want pass through the first available event to the CTA
+ # The default result will be used if first_available_event is None
+ first_available_event = get_first_available_event([link.event.id for link in links if link.event])
+
+ if first_available_event:
+ return {
+ 'title': first_available_event.name,
+ 'description': first_available_event.description,
+ 'icon': '/static/icons/event-icon.jpeg',
+ 'type': 'Event',
+ 'image': cta.image[0].value[0].value.file.url if cta.image else default_data['image'],
+ 'link': first_available_event.get_absolute_url(),
+ }
+ elif series:
+ return series[0]
+ else:
+ # the default CTA will be displayed if series and first available_event are None
+ return default_data
diff --git a/requirements.txt b/requirements.txt
index c1b92c5487..106243a93f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -257,7 +257,7 @@ jsonschema==3.2.0
# directory-components
# drf-spectacular
# great-components
-kombu==5.3.4
+kombu==5.3.5
# via celery
l18n==2021.3
# via wagtail
@@ -418,7 +418,7 @@ soupsieve==2.5
# via beautifulsoup4
sphinx==1.8.6
# via -r requirements.in
-sphinxcontrib-serializinghtml==1.1.5
+sphinxcontrib-serializinghtml==1.1.10
# via sphinxcontrib-websupport
sphinxcontrib-websupport==1.2.4
# via sphinx
diff --git a/requirements_test.in b/requirements_test.in
index 6ab07889c6..0466697af4 100644
--- a/requirements_test.in
+++ b/requirements_test.in
@@ -25,7 +25,7 @@ browserstack-sdk==1.12.0
# Code quality
# -------------
-black
+black==23.12.1
blacken-docs==1.6.0
isort==5.12.0
flake8==6.1.0
diff --git a/requirements_test.txt b/requirements_test.txt
index 66a0f03d21..66e15bb6a6 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -6,10 +6,6 @@
#
--no-binary psycopg2
-aiohttp==3.9.1
- # via black
-aiosignal==1.3.1
- # via aiohttp
airtable-python-wrapper==0.13.0
# via -r requirements.in
alabaster==0.7.16
@@ -36,12 +32,9 @@ astroid==3.0.2
asttokens==2.4.1
# via stack-data
async-timeout==4.0.3
- # via
- # aiohttp
- # redis
+ # via redis
attrs==23.2.0
# via
- # aiohttp
# allure-python-commons
# jsonschema
# outcome
@@ -59,7 +52,7 @@ beautifulsoup4==4.11.2
# wagtail
billiard==3.6.4.0
# via celery
-black==24.1a1
+black==23.12.1
# via
# -r requirements_test.in
# blacken-docs
@@ -340,10 +333,6 @@ freetype-py==2.3.0
# rlpycairo
freezegun==1.1.0
# via -r requirements_test.in
-frozenlist==1.4.1
- # via
- # aiohttp
- # aiosignal
geoip2==2.9.0
# via -r requirements.in
gevent==23.9.1
@@ -382,7 +371,6 @@ idna==3.6
# via
# requests
# trio
- # yarl
imagesize==1.4.1
# via sphinx
importlib-metadata==7.0.1
@@ -426,7 +414,7 @@ jsonschema==3.2.0
# directory-components
# drf-spectacular
# great-components
-kombu==5.3.4
+kombu==5.3.5
# via celery
l18n==2021.3
# via wagtail
@@ -461,10 +449,6 @@ monotonic==1.6
# directory-client-core
msgpack==1.0.7
# via locust
-multidict==6.0.4
- # via
- # aiohttp
- # yarl
mypy-extensions==1.0.0
# via black
nodeenv==1.8.0
@@ -734,7 +718,7 @@ soupsieve==2.5
# via beautifulsoup4
sphinx==1.8.6
# via -r requirements.in
-sphinxcontrib-serializinghtml==1.1.5
+sphinxcontrib-serializinghtml==1.1.10
# via sphinxcontrib-websupport
sphinxcontrib-websupport==1.2.4
# via sphinx
@@ -880,8 +864,6 @@ wsproto==1.2.0
# via trio-websocket
xhtml2pdf==0.2.13
# via -r requirements.in
-yarl==1.9.4
- # via aiohttp
zipp==3.17.0
# via importlib-metadata
zope-event==5.0
diff --git a/sso_profile/business_profile/forms.py b/sso_profile/business_profile/forms.py
index e1cfaff375..946da3b5ce 100644
--- a/sso_profile/business_profile/forms.py
+++ b/sso_profile/business_profile/forms.py
@@ -166,7 +166,7 @@ class CaseStudyRichMediaForm(DynamicHelptextFieldsMixin, forms.Form):
'create_help_text': image_help_text_create,
'update_help_text': image_help_text_update,
'create_label': 'Upload a main image for this case study',
- 'update_label': ('Replace the main image for this case study (optional)'),
+ 'update_label': 'Replace the main image for this case study (optional)',
},
{
'field_name': 'image_two',
diff --git a/sso_profile/enrolment/views.py b/sso_profile/enrolment/views.py
index b81ea929dc..771df99df2 100644
--- a/sso_profile/enrolment/views.py
+++ b/sso_profile/enrolment/views.py
@@ -279,8 +279,8 @@ def done(self, form_list, form_dict, **kwargs):
'company_name': data['company_name'],
'name': self.request.user.full_name,
'email': self.request.user.email,
- 'profile_remove_member_url': (
- self.request.build_absolute_uri(reverse('sso_profile:business-profile-admin-tools'))
+ 'profile_remove_member_url': self.request.build_absolute_uri(
+ reverse('sso_profile:business-profile-admin-tools')
),
'report_abuse_url': urls.domestic.FEEDBACK,
},
diff --git a/tests/unit/contact/test_helpers.py b/tests/unit/contact/test_helpers.py
index 7a5734c0ae..f75a87073f 100644
--- a/tests/unit/contact/test_helpers.py
+++ b/tests/unit/contact/test_helpers.py
@@ -42,7 +42,7 @@ def all_offices():
'is_match': True,
'region_id': 'east_midlands',
'name': 'DIT East Midlands',
- 'address_street': ('The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park'),
+ 'address_street': 'The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park',
'address_city': 'Leicester',
'address_postcode': 'LE19 1RJ',
'email': 'test+east_midlands@examoke.com',
diff --git a/tests/unit/contact/test_views.py b/tests/unit/contact/test_views.py
index 1dbef940d8..ddfc59bb38 100644
--- a/tests/unit/contact/test_views.py
+++ b/tests/unit/contact/test_views.py
@@ -679,7 +679,7 @@ def test_office_finder_valid(all_office_details, client):
'is_match': True,
'region_id': 'east_midlands',
'name': 'DIT East Midlands',
- 'address_street': ('The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park'),
+ 'address_street': 'The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park',
'address_city': 'Leicester',
'address_postcode': 'LE19 1RJ',
'email': 'test+east_midlands@examoke.com',
@@ -697,7 +697,7 @@ def test_office_finder_valid(all_office_details, client):
'is_match': False,
'region_id': 'west_midlands',
'name': 'DIT West Midlands',
- 'address_street': ('The International Trade Centre, ' '10 New Street, ' 'Midlands Business Park'),
+ 'address_street': 'The International Trade Centre, ' '10 New Street, ' 'Midlands Business Park',
'address_city': 'Birmingham',
'address_postcode': 'B20 1RJ',
'email': 'test+west_midlands@examoke.com',
@@ -1844,7 +1844,7 @@ def test_regional_office_not_displayed_on_confirmation_page(
'is_match': True,
'region_id': 'east_midlands',
'name': 'DIT East Midlands',
- 'address_street': ('The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park'),
+ 'address_street': 'The International Trade Centre, ' '5 Merus Court, ' 'Meridian Business Park',
'address_city': 'Leicester',
'address_postcode': 'LE19 1RJ',
'email': 'test+east_midlands@examoke.com',
diff --git a/tests/unit/domestic/management/commands/test_organise_country_guide_ctas.py b/tests/unit/domestic/management/commands/test_organise_country_guide_ctas.py
index e7bad06ab3..b3a945d40d 100644
--- a/tests/unit/domestic/management/commands/test_organise_country_guide_ctas.py
+++ b/tests/unit/domestic/management/commands/test_organise_country_guide_ctas.py
@@ -11,11 +11,14 @@
def test_organise_ctas(domestic_homepage):
ctas = {
'intro_cta_one_title': 'View live export opportunities for Antigua and Barbuda',
- 'intro_cta_one_link': 'https://www.great.gov.uk/export-opportunities/opportunities?s=&areas%5B%5D=antigua-and'
- '-barbuda&commit=Find+opportunities',
+ 'intro_cta_one_link': (
+ 'https://www.great.gov.uk/export-opportunities/opportunities?s=&areas%5B%5D=antigua-and'
+ '-barbuda&commit=Find+opportunities'
+ ),
'intro_cta_two_title': 'Find an online marketplace in Antigua and Barbuda',
- 'intro_cta_two_link': 'https://www.great.gov.uk/selling-online-overseas/markets/results/?category_id'
- '=&country_id=344&commit=',
+ 'intro_cta_two_link': (
+ 'https://www.great.gov.uk/selling-online-overseas/markets/results/?category_id' '=&country_id=344&commit='
+ ),
'intro_cta_three_title': 'Find export events for Antigua and Barbuda',
'intro_cta_three_link': 'https://www.events.great.gov.uk/ehome/index.php?eventid=200183029&',
'intro_cta_four_title': '',
diff --git a/tests/unit/domestic/management/commands/test_update_factsheets_cta_links.py b/tests/unit/domestic/management/commands/test_update_factsheets_cta_links.py
index 421f9c6b5a..f53c1f708a 100644
--- a/tests/unit/domestic/management/commands/test_update_factsheets_cta_links.py
+++ b/tests/unit/domestic/management/commands/test_update_factsheets_cta_links.py
@@ -37,8 +37,10 @@
'title': 'Trade and investment factsheets: Ivory Coast',
},
{
- 'url': 'https://assets.publishing.service.gov.uk/127'
- '/british-indian-ocean-territory-factsheet-2022-02-18.pdf',
+ 'url': (
+ 'https://assets.publishing.service.gov.uk/127'
+ '/british-indian-ocean-territory-factsheet-2022-02-18.pdf'
+ ),
'title': 'Trade and investment factsheets: British Indian Ocean Territory',
},
{
diff --git a/tests/unit/domestic/test_models.py b/tests/unit/domestic/test_models.py
index 3820aaff2c..eb4998bbb6 100644
--- a/tests/unit/domestic/test_models.py
+++ b/tests/unit/domestic/test_models.py
@@ -371,8 +371,10 @@ def test_fact_sheet_columns(
},
{
'title': 'Check for trade barriers',
- 'link': 'https://www.check-international-trade-barriers.service.gov.uk/barriers/'
- '?resolved=0&location=fr',
+ 'link': (
+ 'https://www.check-international-trade-barriers.service.gov.uk/barriers/'
+ '?resolved=0&location=fr'
+ ),
},
],
),
@@ -768,7 +770,7 @@ def test_base_content_page__get_breadcrumbs(
{
'title': article_page.title,
'url': article_page.url,
- }
+ },
# NB: article_page IS in this list
]
diff --git a/tests/unit/learn/factories.py b/tests/unit/learn/factories.py
index b33dbc0d3b..ae3e778ab2 100644
--- a/tests/unit/learn/factories.py
+++ b/tests/unit/learn/factories.py
@@ -15,3 +15,13 @@ class Meta:
class RelatedContentCTASnippetFactory(DjangoModelFactory):
class Meta:
model = models.RelatedContentCTA
+
+
+class UKEACTASnippetFactory(DjangoModelFactory):
+ class Meta:
+ model = models.UKEACTA
+
+
+class EventOrderableFactory(DjangoModelFactory):
+ class Meta:
+ model = models.EventOrderable
diff --git a/tests/unit/learn/test_templatetags.py b/tests/unit/learn/test_templatetags.py
index 28b8f7db16..29f039d339 100644
--- a/tests/unit/learn/test_templatetags.py
+++ b/tests/unit/learn/test_templatetags.py
@@ -1,9 +1,15 @@
import pytest
+from django.utils import timezone
from wagtail.models import Page
from wagtail_factories import PageChooserBlockFactory
-from learn.templatetags.helpers import get_cta_attributes
-from .factories import RelatedContentCTASnippetFactory
+from learn.templatetags.helpers import get_article_cta_attributes, get_cta_attributes
+from tests.unit.export_academy.factories import EventFactory
+from .factories import (
+ EventOrderableFactory,
+ RelatedContentCTASnippetFactory,
+ UKEACTASnippetFactory,
+)
@pytest.mark.parametrize(
@@ -67,3 +73,52 @@ def test_get_cta_attributes(domestic_site, link_text, type, url, expected):
cta_attrs = get_cta_attributes(cta)
assert cta_attrs == expected
+
+
+@pytest.mark.parametrize(
+ 'name, events, expected',
+ (
+ (
+ 'test',
+ None,
+ {
+ 'image': '/static/images/ukea-landing.png',
+ 'icon': '/static/icons/hand.svg',
+ 'type': 'Service',
+ },
+ ),
+ (
+ 'test',
+ 1,
+ {
+ 'image': '/static/images/ukea-landing.png',
+ 'icon': '/static/icons/event-icon.jpeg',
+ 'type': 'Event',
+ },
+ ),
+ (
+ 'test',
+ 2,
+ {
+ 'image': '/static/images/ukea-landing.png',
+ 'icon': '/static/icons/event-icon.jpeg',
+ 'type': 'Event',
+ },
+ ),
+ ),
+)
+@pytest.mark.django_db
+def test_get_article_cta_attributes(root_page, name, events, expected):
+ cta = UKEACTASnippetFactory(name=name)
+
+ if events:
+ for loop in range(events):
+ delta = timezone.now() + timezone.timedelta(days=1 + loop)
+ event_obj = EventFactory(start_date=delta, live=delta, completed=None)
+ EventOrderableFactory(page=cta, event=event_obj)
+
+ cta_attrs = get_article_cta_attributes(cta)
+
+ assert cta_attrs['image'] == expected['image']
+ assert cta_attrs['icon'] == expected['icon']
+ assert cta_attrs['type'] == expected['type']
diff --git a/tests/unit/sso_profile/enrolment/test_helpers.py b/tests/unit/sso_profile/enrolment/test_helpers.py
index bc308391c1..8a34d2d342 100644
--- a/tests/unit/sso_profile/enrolment/test_helpers.py
+++ b/tests/unit/sso_profile/enrolment/test_helpers.py
@@ -145,7 +145,7 @@ def test_notify_company_admins_member_joined_ok(mock_submit):
'form_url': 'the/form/url',
'sender': {},
'spam_control': {},
- 'template_id': (settings.GOV_NOTIFY_NEW_MEMBER_REGISTERED_TEMPLATE_ID),
+ 'template_id': settings.GOV_NOTIFY_NEW_MEMBER_REGISTERED_TEMPLATE_ID,
'email_address': 'admin@xyzcorp.com',
},
}