diff --git a/account/templates/templated_email/junior_approval_mail.email b/account/templates/templated_email/junior_approval_mail.email new file mode 100644 index 0000000..fd3c19f --- /dev/null +++ b/account/templates/templated_email/junior_approval_mail.email @@ -0,0 +1,23 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + Approval Email +{% endblock %} + +{% block plain %} + + +

+ Hi {{full_name}}, +

+ + + + +

+ Please approve {{full_name}} for accessing the Zod bank platform as a junior. + +

+ + +{% endblock %} diff --git a/account/templates/templated_email/junior_notification_email.email b/account/templates/templated_email/junior_notification_email.email new file mode 100644 index 0000000..ea9c214 --- /dev/null +++ b/account/templates/templated_email/junior_notification_email.email @@ -0,0 +1,24 @@ +{% extends "templated_email/email_base.email" %} + +{% block subject %} + Invitation Email +{% endblock %} + +{% block plain %} + + +

+ Hi {{full_name}}, +

+ + + + +

+ You are receiving this email for join the ZOD bank platform. Please use {{ url }} link to join the platform. +
Your credentials are:- username = {{email}} and password {{password}}

Below are the steps to complete the account and how to use this platform. + +

+ + +{% endblock %} diff --git a/account/utils.py b/account/utils.py index 17f1bf5..3b74d24 100644 --- a/account/utils.py +++ b/account/utils.py @@ -6,6 +6,7 @@ from rest_framework.response import Response """Third party Django app""" from templated_email import send_templated_mail import jwt +import string from datetime import datetime from calendar import timegm from uuid import uuid4 @@ -179,3 +180,10 @@ def get_token(data: dict = dict): 'access': access, 'refresh': refresh } + + +def generate_alphanumeric_code(length): + alphabet = string.ascii_letters + string.digits + code = ''.join(secrets.choice(alphabet) for _ in range(length)) + return code + diff --git a/account/views.py b/account/views.py index c2739d6..aab69fb 100644 --- a/account/views.py +++ b/account/views.py @@ -297,6 +297,7 @@ class UserLogin(viewsets.ViewSet): email_verified.otp = otp email_verified.save() data.update({"email_otp":otp}) + send_otp_email(username, otp) return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified}, response_status=status.HTTP_200_OK) data.update({"is_email_verified": is_verified}) diff --git a/base/constants.py b/base/constants.py index c1f48c3..c2458f8 100644 --- a/base/constants.py +++ b/base/constants.py @@ -51,6 +51,11 @@ SIGNUP_METHODS = ( ('2', 'google'), ('3', 'apple') ) +"""relationship""" +RELATIONSHIP = ( + ('1', 'parent'), + ('2', 'legal_guardian') +) PENDING = 1 IN_PROGRESS = 2 REJECTED = 3 @@ -69,5 +74,3 @@ BYTE_IMAGE_SIZE = 1024 # validate file size MAX_FILE_SIZE = 1024 * 1024 * 5 - - diff --git a/base/messages.py b/base/messages.py index 7810bfc..dfdf2cd 100644 --- a/base/messages.py +++ b/base/messages.py @@ -88,7 +88,8 @@ SUCCESS_CODE = { "3017": "Profile image update successfully", "3018": "Task created successfully", "3019": "Support Email sent successfully", - "3020": "Logged out successfully." + "3020": "Logged out successfully.", + "3021": "Add junior successfully" } STATUS_CODE_ERROR = { diff --git a/junior/migrations/0011_junior_relationship.py b/junior/migrations/0011_junior_relationship.py new file mode 100644 index 0000000..45defb0 --- /dev/null +++ b/junior/migrations/0011_junior_relationship.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-12 06:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0010_junior_signup_method'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='relationship', + field=models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True), + ), + ] diff --git a/junior/migrations/0012_junior_is_invited.py b/junior/migrations/0012_junior_is_invited.py new file mode 100644 index 0000000..f36f9b4 --- /dev/null +++ b/junior/migrations/0012_junior_is_invited.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-07-12 10:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('junior', '0011_junior_relationship'), + ] + + operations = [ + migrations.AddField( + model_name='junior', + name='is_invited', + field=models.BooleanField(default=False), + ), + ] diff --git a/junior/models.py b/junior/models.py index c150d20..58b1ae3 100644 --- a/junior/models.py +++ b/junior/models.py @@ -4,7 +4,7 @@ from django.db import models from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField """Import django app""" -from base.constants import GENDERS, SIGNUP_METHODS +from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP User = get_user_model() # Create your models here. @@ -19,6 +19,9 @@ class Junior(models.Model): gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None) dob = models.DateField(max_length=15, null=True, blank=True, default=None) image = models.URLField(null=True, blank=True, default=None) + """relationship""" + relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True, + default='1') """Sign up method""" signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1') """Codes""" @@ -26,6 +29,8 @@ class Junior(models.Model): guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True) referral_code = models.CharField(max_length=10, null=True, blank=True, default=None) referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None) + """invited junior""" + is_invited = models.BooleanField(default=False) """Profile activity""" is_active = models.BooleanField(default=True) is_complete_profile = models.BooleanField(default=False) diff --git a/junior/serializers.py b/junior/serializers.py index 13aa594..ce38851 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -4,12 +4,19 @@ from rest_framework import serializers from django.contrib.auth.models import User from django.db import transaction import random +from django.utils import timezone +from rest_framework_simplejwt.tokens import RefreshToken + """Import django app""" +from account.utils import send_otp_email from junior.models import Junior, JuniorPoints -from guardian.utils import upload_image_to_alibaba +from guardian.tasks import generate_otp from base.messages import ERROR_CODE, SUCCESS_CODE -from guardian.models import Guardian, JuniorTask from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED +from guardian.models import Guardian, JuniorTask +from account.models import UserEmailOtp +from junior.utils import junior_notification_email, junior_approval_mail + class ListCharField(serializers.ListField): """Serializer for Array field""" @@ -238,3 +245,54 @@ class JuniorProfileSerializer(serializers.ModelSerializer): fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method'] + +class AddJuniorSerializer(serializers.ModelSerializer): + """Add junior serializer""" + auth_token = serializers.SerializerMethodField('get_auth_token') + + def get_auth_token(self, obj): + """auth token""" + refresh = RefreshToken.for_user(obj) + access_token = str(refresh.access_token) + return access_token + + class Meta(object): + """Meta info""" + model = Junior + fields = ['gender','dob', 'relationship', 'auth_token'] + + + def create(self, validated_data): + """ create junior""" + with transaction.atomic(): + email = self.context['email'] + guardian = self.context['user'] + full_name = self.context['first_name'] + ' ' + self.context['last_name'] + guardian_data = Guardian.objects.filter(user__username=guardian).last() + user_data = User.objects.create(username=email, email=email, + first_name=self.context['first_name'], + last_name=self.context['last_name']) + + password = User.objects.make_random_password() + user_data.set_password(password) + user_data.save() + junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), + dob=validated_data.get('dob'), is_invited=True, + relationship=validated_data.get('relationship'), + junior_code=''.join([str(random.randrange(9)) for _ in range(4)]), + referral_code=''.join([str(random.randrange(9)) for _ in range(4)]), + referral_code_used=guardian_data.referral_code) + """Generate otp""" + otp_value = generate_otp() + expiry_time = timezone.now() + timezone.timedelta(days=1) + UserEmailOtp.objects.create(email=email, otp=otp_value, + user_type='1', expired_at=expiry_time) + """Send email to the register user""" + send_otp_email(email, otp_value) + """Notification email""" + junior_notification_email(email, full_name, email, password) + junior_approval_mail(guardian, full_name) + return junior_data + + + diff --git a/junior/urls.py b/junior/urls.py index 5eb0f4c..3d2fc55 100644 --- a/junior/urls.py +++ b/junior/urls.py @@ -1,7 +1,7 @@ """ Urls files""" """Django import""" from django.urls import path, include -from .views import UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView +from .views import UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView """Third party import""" from rest_framework import routers @@ -15,6 +15,8 @@ router.register('create-junior-profile', UpdateJuniorProfile, basename='profile- router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code') """junior list API""" router.register('junior-list', JuniorListAPIView, basename='junior-list') +"""Add junior list API""" +router.register('add-junior', AddJuniorAPIView, basename='add-junior') """Define url pattern""" urlpatterns = [ path('api/v1/', include(router.urls)), diff --git a/junior/utils.py b/junior/utils.py new file mode 100644 index 0000000..7d19a2d --- /dev/null +++ b/junior/utils.py @@ -0,0 +1,51 @@ +"""Account utils""" +"""Import django""" +from django.conf import settings +from rest_framework import viewsets, status +from rest_framework.response import Response +"""Third party Django app""" +from templated_email import send_templated_mail +import jwt +import string +from datetime import datetime +from calendar import timegm +from uuid import uuid4 +import secrets +from rest_framework import serializers +from junior.models import Junior +from guardian.models import Guardian +from account.models import UserDelete +from base.messages import ERROR_CODE + + + +def junior_notification_email(recipient_email, full_name, email, password): + """Notification email""" + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [recipient_email] + send_templated_mail( + template_name='junior_notification_email.email', + from_email=from_email, + recipient_list=recipient_list, + context={ + 'full_name': full_name, + 'url':'link', + 'email': email, + 'password': password + } + ) + return full_name + +def junior_approval_mail(guardian, full_name): + """junior approval mail""" + from_email = settings.EMAIL_FROM_ADDRESS + recipient_list = [guardian] + send_templated_mail( + template_name='junior_approval_mail.email', + from_email=from_email, + recipient_list=recipient_list, + context={ + 'full_name': full_name + } + ) + return full_name diff --git a/junior/views.py b/junior/views.py index fa3b57c..d0e689f 100644 --- a/junior/views.py +++ b/junior/views.py @@ -3,7 +3,7 @@ from rest_framework import viewsets, status from rest_framework.permissions import IsAuthenticated """Django app import""" from junior.models import Junior -from .serializers import CreateJuniorSerializer, JuniorDetailListSerializer +from .serializers import CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer from guardian.models import Guardian from base.messages import ERROR_CODE, SUCCESS_CODE from account.utils import custom_response, custom_error_response @@ -61,3 +61,18 @@ class JuniorListAPIView(viewsets.ModelViewSet): queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code)) serializer = JuniorDetailListSerializer(queryset, many=True) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) + +class AddJuniorAPIView(viewsets.ModelViewSet): + """Add Junior by guardian""" + queryset = Junior.objects.all() + serializer_class = AddJuniorSerializer + permission_classes = [IsAuthenticated] + def create(self, request, *args, **kwargs): + """ junior list""" + info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'], + 'last_name': request.data['last_name']} + serializer = AddJuniorSerializer(data=request.data, context=info) + if serializer.is_valid(): + serializer.save() + return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK) + return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)