Skip to content

Commit

Permalink
Merge pull request #3827 from uktrade/develop
Browse files Browse the repository at this point in the history
Release PR
  • Loading branch information
divyaparameswaran authored Feb 20, 2025
2 parents 721ad3e + 5a7d610 commit 36ed249
Show file tree
Hide file tree
Showing 27 changed files with 347 additions and 275 deletions.
9 changes: 0 additions & 9 deletions activitystream/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,3 @@ class ActivityStreamCmsContentFilter(FilterSet):
def filter_after(self, queryset, name, value):
value = value or '0'
return queryset.filter(id__gt=value)


class ActivityStreamHCSATFilter(FilterSet):
after = CharFilter(method='filter_after')

def filter_after(self, queryset, name, value):
value = value or '0.000000'
after_ts = datetime.datetime.fromtimestamp(float(value), tz=datetime.timezone.utc)
return queryset.filter(modified__gt=after_ts)
11 changes: 0 additions & 11 deletions activitystream/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,3 @@ def paginate_queryset(self, queryset, request, view=None):
self.next_value = page[-1].id if page else ''
self.request = request
return page


class ActivityStreamHCSATPagination(ActivityStreamBasePagination):
page_size = 100

def paginate_queryset(self, queryset, request, view=None):
self.has_next = queryset.count() > self.page_size
page = list(queryset[: self.page_size])
self.next_value = page[-1].modified.timestamp() if page else ''
self.request = request
return page
45 changes: 1 addition & 44 deletions activitystream/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from wagtail.models import Page
from wagtail.rich_text import RichText, get_text_for_indexing

from core.models import HCSAT, GreatMedia, MicrositePage
from core.models import GreatMedia, MicrositePage
from domestic.models import ArticlePage
from export_academy.models import (
Booking,
Expand Down Expand Up @@ -539,46 +539,3 @@ def to_representation(self, instance):
'videoTitle': instance.video.title if instance.video else None,
},
}


class ActivityStreamDomesticHCSATUserFeedbackDataSerializer(serializers.ModelSerializer):
"""
Domestic HCSAT Feedback Data serializer for activity stream.
"""

feedback_submission_date = serializers.DateTimeField(source='created') # noqa: N815
url = serializers.CharField(source='URL') # noqa: N815

class Meta:
model = HCSAT
fields = [
'id',
'feedback_submission_date',
'url',
'user_journey',
'satisfaction_rating',
'experienced_issues',
'other_detail',
'service_improvements_feedback',
'likelihood_of_return',
'service_name',
'service_specific_feedback',
'service_specific_feedback_other',
]

def to_representation(self, instance):
"""
Prefix field names to match activity stream format
"""
prefix = 'dit:domestic:HCSATFeedbackData'
type = 'Update'

return {
'id': f'{prefix}:{instance.id}:{type}',
'type': f'{type}',
'object': {
'id': f'{prefix}:{instance.id}',
'type': prefix,
**{f'{k}': v for k, v in super().to_representation(instance).items()},
},
}
5 changes: 0 additions & 5 deletions activitystream/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,4 @@
skip_ga360(activitystream.views.ActivityStreamExportAcademyVideoOnDemandPageTrackingView.as_view()),
name='ukea-videoondemandpagetracking',
),
path(
'domestic-hcsats/',
skip_ga360(activitystream.views.ActivityStreamDomesticHCSATFeedbackDataView.as_view()),
name='domestic-hcsats',
),
]
20 changes: 1 addition & 19 deletions activitystream/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,15 @@
ActivityStreamCmsContentFilter,
ActivityStreamExpandYourBusinessFilter,
ActivityStreamExportAcademyFilter,
ActivityStreamHCSATFilter,
PageFilter,
)
from activitystream.pagination import (
ActivityStreamCmsContentPagination,
ActivityStreamExpandYourBusinessPagination,
ActivityStreamExportAcademyPagination,
ActivityStreamHCSATPagination,
)
from activitystream.serializers import (
ActivityStreamCmsContentSerializer,
ActivityStreamDomesticHCSATUserFeedbackDataSerializer,
ActivityStreamExpandYourBusinessTriageDataSerializer,
ActivityStreamExpandYourBusinessUserDataSerializer,
ActivityStreamExportAcademyBookingSerializer,
Expand All @@ -38,7 +35,7 @@
ActivityStreamExportAcademyVideoOnDemandPageTrackingSerializer,
PageSerializer,
)
from core.models import HCSAT, MicrositePage
from core.models import MicrositePage
from domestic.models import ArticlePage, CountryGuidePage
from export_academy.models import (
Booking,
Expand Down Expand Up @@ -259,18 +256,3 @@ class ActivityStreamExportAcademyBaseView(ActivityStreamBaseView):

def get_queryset(self):
return self.queryset.order_by('id')


class ActivityStreamHCSATBaseView(ActivityStreamBaseView):
filterset_class = ActivityStreamHCSATFilter
pagination_class = ActivityStreamHCSATPagination

def get_queryset(self):
return self.queryset.order_by('id')


class ActivityStreamDomesticHCSATFeedbackDataView(ActivityStreamHCSATBaseView):
"""View to list domestic HCSAT feedback data for the activity stream"""

queryset = HCSAT.objects.all()
serializer_class = ActivityStreamDomesticHCSATUserFeedbackDataSerializer
1 change: 1 addition & 0 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
'core.middleware.StoreUserExpertiseMiddleware',
'core.middleware.CheckGATags',
'core.middleware.HHTPHeaderDisallowEmbeddingMiddleware',
'core.middleware.GA4TrackingMiddleware',
# 'directory_sso_api_client.middleware.AuthenticationMiddleware',
'great_components.middleware.NoCacheMiddlware',
'csp.middleware.CSPMiddleware',
Expand Down
9 changes: 9 additions & 0 deletions core/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum

SERVICE_NAME = 'great-cms'

VIDEO_DURATION_DATA_ATTR_NAME = 'data-v-duration'
Expand Down Expand Up @@ -1437,6 +1439,13 @@
'ZM',
]


class HCSatStage(Enum):
NOT_STARTED = 0 # Stage 0: HCSAT has not been started
SUBMITTED = 1 # Stage 1: HCSAT satisfaction has been submitted
COMPLETED = 2 # Stage 2: HCSAT has been completed


EU_TRAVEL_ADVICE_URLS = (
('Austria', 'https://www.gov.uk/guidance/travel-to-austria-for-work'),
('Belgium', 'https://www.gov.uk/guidance/travel-to-belgium-for-work'),
Expand Down
10 changes: 9 additions & 1 deletion core/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
TRADE_BARRIERS_BY_MARKET,
TRADE_BARRIERS_BY_SECTOR,
)
from core.models import CuratedListPage
from core.models import HCSAT, CuratedListPage
from core.serializers import parse_opportunities
from directory_api_client import api_client
from directory_constants import choices, company_types
Expand Down Expand Up @@ -910,3 +910,11 @@ def get_sectors_and_sic_sectors_file():
deserialised_data = json.load(json_data)
json_data.close()
return deserialised_data


def send_hcsat_feedback(data: HCSAT) -> None:
action = actions.HCSatAction(
form_url=str(),
)
response = action.save(data)
response.raise_for_status()
40 changes: 40 additions & 0 deletions core/management/commands/submit_hcsat_feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import logging

from django.core.management import BaseCommand

from core.helpers import send_hcsat_feedback
from core.models import HCSAT

logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = 'Submit HCSAT Feedback to Forms API'

def handle(self, *args, **options):
try:
data = {'hcsat_feedback_entries': []}
for feedback in HCSAT.objects.all():
data['hcsat_feedback_entries'].append(
{
'id': feedback.pk,
'feedback_submission_date': feedback.created.strftime('%Y-%m-%d %H:%M:%S'),
'url': feedback.URL,
'user_journey': feedback.user_journey,
'satisfaction_rating': feedback.satisfaction_rating,
'experienced_issues': feedback.experienced_issues,
'other_detail': feedback.other_detail,
'service_improvements_feedback': feedback.service_improvements_feedback,
'likelihood_of_return': feedback.likelihood_of_return,
'service_name': feedback.service_name,
'service_specific_feedback': feedback.service_specific_feedback,
'service_specific_feedback_other': feedback.service_specific_feedback_other,
},
)
send_hcsat_feedback(data)
except Exception as e:
logger.exception(f'Submit HCSAT Feedback to Forms API Exception {str(e)}')
raise e
else:
HCSAT.objects.all().delete()
self.stdout.write(self.style.SUCCESS('All done, bye!'))
16 changes: 16 additions & 0 deletions core/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from core import helpers
from core.fern import Fern
from core.tasks import send_to_ga4
from sso.models import BusinessSSOUser

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -279,3 +280,18 @@ def __call__(self, request):
response = super().__call__(request)
response['X-Permitted-Cross-Domain-Policies'] = 'none'
return response


# Middleware class to handle GA tracking asynchronously
class GA4TrackingMiddleware(MiddlewareMixin):
def process_response(self, request, response):

if not 200 <= response.status_code < 300:
return response

if getattr(response, 'skip_ga360', False):
return response

if settings.GA4_API_SECRET and settings.GA4_MEASUREMENT_ID:
send_to_ga4.delay(request.path, dict(request.headers))
return response
14 changes: 8 additions & 6 deletions core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from great_components import helpers as great_components_helpers

from core import cms_slugs, models
from core.constants import HCSatStage

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -246,6 +247,7 @@ def get_hcsat(self, request, service):
return None

def set_csat_and_stage(self, request, ctx, hcsat_service_name, form):

hcsat = self.get_hcsat(request, hcsat_service_name)

# all csat instances use the same form object, so customise initial heading depending on service
Expand All @@ -255,12 +257,12 @@ def set_csat_and_stage(self, request, ctx, hcsat_service_name, form):
ctx['hcsat_form'] = form
ctx['hcsat'] = hcsat

if hcsat and hcsat.stage == 2:
ctx['hcsat_form_stage'] = 2
hcsat.stage = 0
if hcsat and hcsat.stage == HCSatStage.COMPLETED.value:
ctx['hcsat_form_stage'] = HCSatStage.COMPLETED.value
hcsat.stage = HCSatStage.NOT_STARTED.value
hcsat.save()
else:
ctx['hcsat_form_stage'] = hcsat.stage if hcsat else 0
ctx['hcsat_form_stage'] = hcsat.stage if hcsat else HCSatStage.NOT_STARTED.value

return ctx

Expand Down Expand Up @@ -322,7 +324,7 @@ def post(self, request, *args, **kwargs):
Redirect user if 'cancelButton' is found in the POST data
"""
if hcsat:
hcsat.stage = 2
hcsat.stage = HCSatStage.COMPLETED.value
hcsat.save()
return HttpResponseRedirect(self.get_success_url(request))

Expand Down Expand Up @@ -360,7 +362,7 @@ def form_valid(self, form, request):

# js version handles form progression in js file, so keep on 0 for reloads
if 'js_enabled' in request.get_full_path():
hcsat.stage = 0
hcsat.stage = HCSatStage.NOT_STARTED.value
js_enabled = True

# if in second part of form (satisfaction=None) or not given in first part, persist existing satisfaction rating
Expand Down
8 changes: 5 additions & 3 deletions core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
BACKLINK_QUERYSTRING_NAME,
RICHTEXT_FEATURES__MINIMAL,
RICHTEXT_FEATURES__REDUCED,
HCSatStage,
)
from core.context import get_context_provider
from core.utils import PageTopicHelper, get_first_lesson, persist_language_to_url
Expand Down Expand Up @@ -1047,7 +1048,7 @@ def post(self, request, *args, **kwargs):
Redirect user if 'cancelButton' is found in the POST data
"""
if hcsat:
hcsat.stage = 2
hcsat.stage = HCSatStage.COMPLETED.value
hcsat.save()
return HttpResponseRedirect(self.get_success_url(request))

Expand Down Expand Up @@ -1077,7 +1078,7 @@ def form_valid(self, form, request):

# js version handles form progression in js file, so keep on 0 for reloads
if 'js_enabled' in request.get_full_path():
hcsat.stage = 0
hcsat.stage = HCSatStage.NOT_STARTED.value
js_enabled = True

# if in second part of form (satisfaction=None) or not given in first part, persist existing satisfaction rating
Expand Down Expand Up @@ -2536,7 +2537,8 @@ def save(self, *args, **kwargs):
elif current[0].stage == current_hcsat_stage and current_hcsat_stage < 2:
self.stage = current_hcsat_stage + 1
if js_enabled:
self.stage = 0
self.stage = HCSatStage.NOT_STARTED.value

super(HCSAT, self).save(*args)


Expand Down
13 changes: 12 additions & 1 deletion core/static/javascript/dit.tagging.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ if (!String.prototype.includes) {
var dit = dit || {}
dit.tagging = dit.tagging || {};
dit.tagging.base = new function() {
var lastVideoPercent = -1;

this.init = function(debug_mode) {
$(document).ready(function() {
addTaggingForLinks();
Expand Down Expand Up @@ -48,6 +50,7 @@ dit.tagging.base = new function() {
.on('play', function() { sendVideoEvent($(this), 'play') })
.on('pause', function() { sendVideoEvent($(this), 'pause') })
.on('ended', function() { sendVideoEvent($(this), 'ended') })
.on('timeupdate', function() { sendVideoEvent($(this), 'progress') });
}

function addTaggingForForms() {
Expand All @@ -70,7 +73,7 @@ dit.tagging.base = new function() {

function sendVideoEvent(video, action) {
var videoPercent = 0
var videoStatus = 'progress'
var videoStatus = action
const currentPercent = calculateVideoPercent(video[0]);
if (currentPercent < 25)
videoPercent = 0;
Expand All @@ -86,6 +89,14 @@ dit.tagging.base = new function() {
videoPercent = 100
}

if (videoPercent !== lastVideoPercent) {
sendVideoEventDetails(video, videoStatus, videoPercent, action)
lastVideoPercent = videoPercent;
}


}
function sendVideoEventDetails(video, videoStatus, videoPercent, action) {
var type = video.data('ga-type') || 'video';
var element = video.data('ga-element') || inferElement(video);
var value = video.data('ga-value') || inferVideoValue(video);
Expand Down
Loading

0 comments on commit 36ed249

Please sign in to comment.