Skip to content

Commit

Permalink
Create onplatform training with json
Browse files Browse the repository at this point in the history
Added a serializer which will be used to allow admins to create a new
 training or update the existing training.

To track the updates to training(manage versions), we are using semantic versioning. If the  updated training has a major update
eg: from 1.9 to 2.0, then all the users who did complete the previous
versions will be sent an email asking them to complete the new version
 of training within x days.
  • Loading branch information
superryeti committed Mar 28, 2023
1 parent 83c59f7 commit 1a22c45
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 0 deletions.
16 changes: 16 additions & 0 deletions physionet-django/notification/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,3 +1025,19 @@ def notify_event_participant_application(request, user, registered_user, event):
body = loader.render_to_string('events/email/event_registration.html', context)
# Not resend the email if there was an integrity error
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [user.email], fail_silently=False)


def notify_users_of_training_expiry(user, training, expiry):
"""
Send the training expiry email.
"""

subject = f"{settings.SITE_NAME} Training Validation Notification"
context = {
'name': user.get_full_name(),
'SITE_NAME': settings.SITE_NAME,
'training': training,
'expiry': expiry
}
body = loader.render_to_string('training/email/training_notification.html', context)
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [user, ], fail_silently=False)
164 changes: 164 additions & 0 deletions physionet-django/training/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import datetime
from rest_framework import serializers
from django.db import transaction
from django.utils import timezone

from training.models import OnPlatformTraining, Module, Quiz, QuizChoice, ContentBlock
from user.models import Training, TrainingType
from notification.utility import notify_users_of_training_expiry


NUMBER_OF_DAYS_SET_TO_EXPIRE = 30


class QuizChoiceSerializer(serializers.ModelSerializer):

class Meta:
model = QuizChoice
fields = "__all__"
read_only_fields = ['id', 'quiz']


class QuizSerializer(serializers.ModelSerializer):
choices = QuizChoiceSerializer(many=True)

class Meta:
model = Quiz
fields = "__all__"
read_only_fields = ['id', 'module']


class ContentBlockSerializer(serializers.ModelSerializer):

class Meta:
model = ContentBlock
fields = "__all__"
read_only_fields = ['id', 'module']


class ModuleSerializer(serializers.ModelSerializer):
quizzes = QuizSerializer(many=True)
contents = ContentBlockSerializer(many=True)

class Meta:
model = Module
fields = "__all__"
read_only_fields = ['id', 'training']


class OnPlatformTrainingSerializer(serializers.ModelSerializer):
modules = ModuleSerializer(many=True)

class Meta:
model = OnPlatformTraining
fields = "__all__"
read_only_fields = ['id', 'training_type']


class TrainingTypeSerializer(serializers.ModelSerializer):
op_trainings = OnPlatformTrainingSerializer()

class Meta:
model = TrainingType
fields = "__all__"
read_only_fields = ['id']

def update_training_for_major_version_change(self, instance):
"""
If it is a major version change, it sets all former user trainings
to a reduced date, and informs them all.
"""

trainings = Training.objects.filter(
training_type=instance,
process_datetime__gte=timezone.now() - instance.valid_duration)
_ = trainings.update(
process_datetime=(
timezone.now() - (instance.valid_duration - timezone.timedelta(
days=NUMBER_OF_DAYS_SET_TO_EXPIRE))))

for training in trainings:
notify_users_of_training_expiry(
training.user, instance.name, NUMBER_OF_DAYS_SET_TO_EXPIRE)

def update(self, instance, validated_data):

with transaction.atomic():
op_training = validated_data.pop('op_trainings')
modules = op_training.pop('modules')

op_training['training_type'] = instance

op_training_instance = OnPlatformTraining.objects.create(**op_training)

for module in modules:
quizzes = module.pop('quizzes')
contents = module.pop('contents')

module['training'] = op_training_instance
module_instance = Module.objects.create(**module)

choice_bulk = []
for quiz in quizzes:
choices = quiz.pop('choices')

quiz['module'] = module_instance
q = Quiz(**quiz)
q.save()

for choice in choices:
choice['quiz'] = q
choice_bulk.append(QuizChoice(**choice))

QuizChoice.objects.bulk_create(choice_bulk)

content_bulk = []
for content in contents:
content['module'] = module_instance
content_bulk.append(ContentBlock(**content))
ContentBlock.objects.bulk_create(content_bulk)

if op_training.get("version"):
if str(op_training.get("version")).endswith("0"):
self.update_training_for_major_version_change(instance)

return op_training_instance

def create(self, validated_data):

with transaction.atomic():
op_training = validated_data.pop('op_trainings')
modules = op_training.pop('modules')

op_training['training_type'] = instance = TrainingType.objects.create(**validated_data)

op_training_instance = OnPlatformTraining.objects.create(**op_training)

for module in modules:
quizzes = module.pop('quizzes')
contents = module.pop('contents')

module['training'] = op_training_instance
module_instance = Module.objects.create(**module)

choice_bulk = []
for quiz in quizzes:
choices = quiz.pop('choices')

quiz['module'] = module_instance
q = Quiz(**quiz)
q.save()

for choice in choices:
choice['quiz'] = q
choice_bulk.append(QuizChoice(**choice))

QuizChoice.objects.bulk_create(choice_bulk)

content_bulk = []
for content in contents:
content['module'] = module_instance
content_bulk.append(ContentBlock(**content))
ContentBlock.objects.bulk_create(content_bulk)

return instance
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% load i18n %}{% autoescape off %}{% filter wordwrap:70 %}
Dear {{ name }},

Your training {{ training }} on {{ domain }} will be expiring in {{ expiry }} days. To retain the access it provides, kindly login to your account to retake it.


Regards
The {{ SITE_NAME }} Team
{% endfilter %}{% endautoescape %}

0 comments on commit 1a22c45

Please sign in to comment.