Compare commits

..

41 Commits

Author SHA1 Message Date
22f1d01b94 requirement.txt file 2023-07-03 15:59:37 +05:30
a27c486c43 remove unused code 2023-07-03 15:53:25 +05:30
28cbdf9cfa jira-5 social login 2023-07-03 14:57:07 +05:30
014b5fe183 jira-5 apple login with first and last name 2023-07-03 12:58:06 +05:30
d2498f82ad jira-5 google login and apple login 2023-06-30 21:25:43 +05:30
79ac140ddd jira-3 google login 2023-06-30 16:22:41 +05:30
4a34dab570 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into login_code 2023-06-30 16:10:09 +05:30
245162b913 jira-274 user can use same number multiple time 2023-06-30 15:51:12 +05:30
7376fc555b jira-5 google login 2023-06-30 15:21:00 +05:30
ed2d7895ab Merge pull request #23 from KiwiTechLLC/login_code
reset password msg
2023-06-30 11:17:54 +05:30
c57968b7c0 reset password msg 2023-06-30 11:17:18 +05:30
e42d8af65b Merge pull request #20 from KiwiTechLLC/login_code
email changes
2023-06-29 21:44:33 +05:30
d254c5e5fa email changes 2023-06-29 21:39:36 +05:30
118d73776b Merge pull request #17 from KiwiTechLLC/login_code
jira-7 email
2023-06-29 20:29:51 +05:30
c2713b8214 jira-7 email 2023-06-29 20:26:35 +05:30
4dd95e74fb Merge pull request #16 from KiwiTechLLC/login_code
Login code
2023-06-29 18:22:14 +05:30
70c136dde4 jira-9 changes in setting 2023-06-29 18:21:15 +05:30
88a9e925ed jira-9 changes in setting 2023-06-29 18:20:07 +05:30
3d3ccfb146 jira-9 changes in setting 2023-06-29 18:16:11 +05:30
be6725c66d jira-9 changes in setting 2023-06-29 18:14:49 +05:30
f3c41043e0 Merge pull request #15 from KiwiTechLLC/login_code
jira-4 login after email verified
2023-06-29 18:09:05 +05:30
ea2bd635ca jira-4 login after email verified 2023-06-29 18:03:27 +05:30
eb4cde397f Merge pull request #14 from KiwiTechLLC/GoogleLogin
add requirement file
2023-06-29 13:05:17 +05:30
4864d3b499 add requirement file 2023-06-29 13:03:40 +05:30
ef1f7ebeac Merge pull request #13 from KiwiTechLLC/GoogleLogin
Google login
2023-06-29 12:19:05 +05:30
c1ae88c32f jira-13 image upload changes 2023-06-29 11:49:40 +05:30
9d7f265f40 jira-9 and jira-12 sendgrid and upload images in alibaba bucket 2023-06-29 11:40:04 +05:30
f0a2a4bd4b jira-13 update profile 2023-06-27 21:34:40 +05:30
4a982f1baf changes in setting.py file 2023-06-27 20:44:42 +05:30
d849153bec changes in setting.py file 2023-06-27 20:43:22 +05:30
947119d4c2 Merge pull request #10 from KiwiTechLLC/26june_sprint1
changes in setting.py file
2023-06-27 20:18:12 +05:30
a41e3a6f14 Merge branch 'dev' into 26june_sprint1 2023-06-27 20:18:04 +05:30
7b02b7dfbc changes in setting.py file 2023-06-27 20:16:35 +05:30
3852e9bf03 Merge pull request #9 from KiwiTechLLC/26june_sprint1
26june sprint1
2023-06-27 19:34:27 +05:30
d208e5252c jira-13 update country name 2023-06-27 19:32:57 +05:30
7b6d34f334 setting changes 2023-06-27 17:37:12 +05:30
0006208cf8 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into 26june_sprint1 2023-06-27 17:31:00 +05:30
352de6d630 Merge pull request #8 from KiwiTechLLC/server_demo
changes
2023-06-27 17:28:51 +05:30
019d028cb6 jira-5 google login 2023-06-27 17:22:59 +05:30
e1e5b1838a Merge pull request #7 from KiwiTechLLC/server_demo
changes docker file position
2023-06-27 16:54:39 +05:30
4537f1f4fa changes 2023-06-27 16:45:42 +05:30
24 changed files with 583 additions and 80 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='useremailotp',
name='user_type',
field=models.CharField(blank=True, choices=[('1', 'junior'), ('2', 'guardian'), ('3', 'superuser')], default=None, max_length=15, null=True),
),
]

View File

@ -59,6 +59,7 @@ class UserEmailOtp(models.Model):
"""otp details"""
otp = models.CharField(max_length=10)
is_verified = models.BooleanField(default=False)
user_type = models.CharField(max_length=15, choices=USER_TYPE, null=True, blank=True, default=None)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)

View File

@ -12,8 +12,45 @@ from rest_framework import viewsets, status
from rest_framework.decorators import action
from django.contrib.auth import authenticate, login
from rest_framework_simplejwt.tokens import RefreshToken
from guardian.utils import upload_image_to_alibaba
from .utils import get_token
class GoogleLoginSerializer(serializers.Serializer):
access_token = serializers.CharField(max_length=5000, required=True)
class Meta:
"""meta class"""
fields = ('access_token',)
class UpdateGuardianImageSerializer(serializers.ModelSerializer):
"""Reset Password after verification"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = '__all__'
def update(self, instance, validated_data):
"""update image """
instance.image = validated_data.get('image', instance.image)
instance.save()
return instance
class UpdateJuniorProfileImageSerializer(serializers.ModelSerializer):
"""Reset Password after verification"""
class Meta(object):
"""Meta info"""
model = Junior
fields = '__all__'
def update(self, instance, validated_data):
"""update image """
image = validated_data.get('image', instance.image)
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
instance.image = image_url
instance.save()
return instance
class ResetPasswordSerializer(serializers.Serializer):
"""Reset Password after verification"""
verification_code = serializers.CharField(max_length=10)
@ -25,15 +62,12 @@ class ResetPasswordSerializer(serializers.Serializer):
def create(self, validated_data):
verification_code = validated_data.pop('verification_code')
password = validated_data.pop('password')
print("verification_code===>",verification_code)
print("password===>", password)
user_opt_details = UserEmailOtp.objects.filter(otp=verification_code, is_verified=True).last()
print("user_opt_details===>",user_opt_details)
if user_opt_details:
print("qqqqqqqqqq")
user_details = User.objects.filter(email=user_opt_details.email).last()
if user_details:
print("333333333==>",user_details.password)
if user_details.check_password(password):
raise serializers.ValidationError({"details":ERROR_CODE['2001'],"code":"400", "status":"failed"})
user_details.set_password(password)
user_details.save()
return {'password':password}
@ -53,13 +87,14 @@ class ChangePasswordSerializer(serializers.Serializer):
if self.context.password not in ('', None):
if user.check_password(value):
return value
raise serializers.ValidationError({"error":"Invalid Current password"})
raise serializers.ValidationError({"details":ERROR_CODE['2015']})
def create(self, validated_data):
new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password')
if new_password == current_password:
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
user_details = User.objects.filter(email=self.context).last()
print("user_details==>", user_details)
if user_details:
print("333333333==>",user_details.password)
user_details.set_password(new_password)
user_details.save()
return {'password':new_password}
@ -91,11 +126,13 @@ class GuardianSerializer(serializers.ModelSerializer):
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
access_token = str(refresh.access_token)
return access_token
return get_token()['access']
def get_refresh_token(self, obj):
return get_token()['refresh']
def get_user_type(self, obj):
"""user type"""
@ -116,8 +153,8 @@ class GuardianSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['auth_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'family_name', 'gender', 'dob',
'referral_code', 'is_active', 'is_complete_profile', 'passcode',
fields = ['auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'family_name', 'gender', 'dob',
'referral_code', 'is_active', 'is_complete_profile', 'passcode', 'image',
'created_at', 'updated_at', 'user_type']
@ -128,11 +165,12 @@ class JuniorSerializer(serializers.ModelSerializer):
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
access_token = str(refresh.access_token)
return access_token
return get_token()['access']
def get_refresh_token(self, obj):
return get_token()['refresh']
def get_user_type(self, obj):
return JUNIOR
@ -149,8 +187,8 @@ class JuniorSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
model = Junior
fields = ['auth_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at',
fields = ['auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'user_type']
class EmailVerificationSerializer(serializers.ModelSerializer):

View File

@ -5,21 +5,24 @@ from rest_framework.decorators import api_view
"""Third party import"""
from rest_framework import routers
from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVerification, ReSendEmailOtp,
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView)
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple)
"""Router"""
router = routers.SimpleRouter()
"""API End points with router"""
router.register('user', UserLogin, basename='user')
router.register('admin', UserLogin, basename='admin')
router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
router.register('user-email-verification', UserEmailVerification, basename='user-email-verification')
router.register('resend-email-otp', ReSendEmailOtp, basename='resend-email-otp')
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/forgot-password/', ForgotPasswordAPIView.as_view()),
path('api/v1/reset-password/', ResetPasswordAPIView.as_view()),
path('api/v1/change-password/', ChangePasswordAPIView.as_view())
path('api/v1/change-password/', ChangePasswordAPIView.as_view()),
path('api/v1/update-profile-image/', UpdateProfileImage.as_view()),
path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'),
]

View File

@ -3,10 +3,15 @@
from django.conf import settings
from rest_framework import viewsets, status
from rest_framework.response import Response
from templated_email import send_templated_mail
import jwt
from datetime import datetime
from calendar import timegm
from uuid import uuid4
import secrets
def send_otp_email(recipient_email, otp):
from_email = settings.EMAIL_HOST_USER
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [recipient_email]
send_templated_mail(
template_name='email_otp_verification.email',
@ -36,3 +41,53 @@ def custom_error_response(detail, response_status):
if not detail:
detail = {}
return Response({"error": detail, "status": "failed", "code": response_status})
def get_user_data(attrs):
"""
used to decode token
"""
user_data = jwt.decode(jwt=attrs['token'], options={'verify_signature': False},
algorithms=['RS256'])
return user_data
def generate_jwt_token(token_type: str, now_time: int, data: dict = dict):
"""
used to generate jwt token
"""
if type(data) == type:
data = {}
data.update({
'token_type': token_type,
'iss': 'your_site_url',
'iat': timegm(datetime.utcnow().utctimetuple()),
'jti': uuid4().hex
})
TOKEN_TYPE = ["access", "refresh"]
if token_type == TOKEN_TYPE[1]:
exp = now_time + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME']
else:
exp = now_time + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
data.update({
"exp": timegm(exp.utctimetuple())
})
signing_key = secrets.token_hex(32)
return jwt.encode(payload=data, key=signing_key,
algorithm='HS256')
def get_token(data: dict = dict):
""" create access and refresh token """
now_time = datetime.utcnow()
access = generate_jwt_token('access', now_time, data)
refresh = generate_jwt_token('refresh', now_time, data)
return {
'access': access,
'refresh': refresh
}

View File

@ -2,26 +2,148 @@ from rest_framework import viewsets, status, views
from rest_framework.decorators import action
import random
import logging
import jwt
from django.contrib.auth import authenticate, login
from guardian.models import Guardian
from junior.models import Junior
from account.models import UserProfile, UserPhoneOtp, UserEmailOtp
from django.contrib.auth.models import User
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer)
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.tasks import generate_otp
from django.conf import settings
from account.utils import send_otp_email
from account.utils import custom_response, custom_error_response
from django.core.mail import EmailMessage
from django.core.mail import send_mail
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from templated_email import send_templated_mail
import google.oauth2.credentials
import google.auth.transport.requests
from rest_framework import status
from rest_framework.response import Response
import requests
from django.conf import settings
from .utils import get_token
class GoogleLoginMixin:
def google_login(self, request):
access_token = request.data.get('access_token')
user_type = request.data.get('user_type')
if not access_token:
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
try:
# Validate the access token and obtain the user's email and name
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(
info={
'access_token': access_token,
'token_uri': 'https://oauth2.googleapis.com/token',
'client_id': settings.GOOGLE_CLIENT_ID,
'client_secret': settings.GOOGLE_CLIENT_SECRET,
'refresh_token': None,
}
)
user_info_endpoint = f'https://www.googleapis.com/oauth2/v3/userinfo?access_token={access_token}'
headers = {'Authorization': f'Bearer {credentials.token}'}
response = requests.get(user_info_endpoint, headers=headers)
response.raise_for_status()
user_info = response.json()
email = user_info['email']
first_name = user_info['given_name']
last_name = user_info['family_name']
profile_picture = user_info['picture']
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
# Check if the user exists in your database or create a new user
# ...
user_data = User.objects.filter(email__iexact=email)
if user_data.exists():
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user_data.last()).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
if not User.objects.filter(email__iexact=email).exists():
user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
image=profile_picture)
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture)
serializer = GuardianSerializer(guardian_query)
# Return a JSON response with the user's email and name
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
serializer_class = GoogleLoginSerializer
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return self.google_login(request)
class SigninWithApple(views.APIView):
"""This API is for sign in with Apple for app."""
def post(self, request):
token = request.data.get("access_token")
user_type = request.data.get("user_type")
if not token:
return custom_error_response(ERROR_CODE['2027'], response_status=status.HTTP_400_BAD_REQUEST)
try:
decoded_data = jwt.decode(token, options={"verify_signature": False})
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
if decoded_data.get("email"):
try:
user = User.objects.get(email=decoded_data.get("email"))
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except User.DoesNotExist:
user = User.objects.create(**user_data)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True)
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True)
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except Exception as e:
logging.error(e)
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateProfileImage(views.APIView):
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
if request.data['user_type'] == '1':
junior_query = Junior.objects.filter(auth=request.user).last()
serializer = UpdateJuniorProfileImageSerializer(junior_query, data=request.data, partial=True)
else:
guardian_query = Guardian.objects.filter(user=request.user).last()
serializer = UpdateGuardianImageSerializer(guardian_query, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3017'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ChangePasswordAPIView(views.APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
@ -50,7 +172,7 @@ class ForgotPasswordAPIView(views.APIView):
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = ''.join([str(random.randrange(9)) for _ in range(6)])
# Send the verification code to the user's email
from_email = settings.EMAIL_HOST_USER
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
@ -106,26 +228,26 @@ class UserLogin(viewsets.ViewSet):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
guardian_data = Guardian.objects.filter(user__username=username, is_complete_profile=True).last()
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
if guardian_data:
serializer = GuardianSerializer(guardian_data)
junior_data = Junior.objects.filter(auth__username=username, is_complete_profile=True).last()
serializer = GuardianSerializer(guardian_data).data
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data:
serializer = JuniorSerializer(junior_data)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
serializer = JuniorSerializer(junior_data).data
return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
except Exception as e:
logging.error(e)
user_profile_data = UserProfile.objects.filter(user__username=username).last()
email_verified = UserEmailOtp.objects.filter(email=username).last()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
data = {"auth_token":access_token, "is_profile_complete": False,
"user_role": user_profile_data.user_type,
"user_type": email_verified.user_type,
}
is_verified = False
if email_verified:
@ -172,6 +294,16 @@ class UserEmailVerification(viewsets.ModelViewSet):
if email_data:
email_data.is_verified = True
email_data.save()
if email_data.user_type == '1':
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last()
if junior_data:
junior_data.is_verified = True
junior_data.save()
else:
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last()
if guardian_data:
guardian_data.is_verified = True
guardian_data.save()
refresh = RefreshToken.for_user(user_obj)
access_token = str(refresh.access_token)
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token}, response_status=status.HTTP_200_OK)

View File

@ -24,7 +24,7 @@ ERROR_CODE_REQUIRED = {
# Error code
ERROR_CODE = {
"2000": "Email not found.",
"2001": "Your account has not been verified. Please check your email and verify it.",
"2001": "This is your existing password. Please choose other one",
"2002": "Invalid login credentials.",
"2003": "An account already exists with this email address.",
"2004": "User not found.",
@ -35,7 +35,7 @@ ERROR_CODE = {
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
"2010": "Invalid Link.",
"2011": "Your profile has not been completed yet.",
"2012": "Password and Confirm password should be same.",
"2012": "Phone number already used",
"2013": "Invalid token.",
"2014": "Your old password doesn't match.",
"2015": "Invalid old password.",
@ -45,9 +45,12 @@ ERROR_CODE = {
"2019": "Either File extension or File size doesn't meet the requirements",
"2020": "Enter valid mobile number",
"2021": "Already register",
"2022":"Invalid Guardian code",
"2023":"Invalid user",
"2024":"Email not verified"
"2022": "Invalid Guardian code",
"2023": "Invalid user",
"2024": "Email not verified",
"2025": "Invalid input. Expected a list of strings.",
"2026": "New password should not same as old password",
"2027": "data should contain `identityToken`"
}
SUCCESS_CODE = {
# Success code for password
@ -75,7 +78,8 @@ SUCCESS_CODE = {
"3013": "Valid Guardian code",
"3014": "Password has been updated successfully.",
"3015": "Verification code sent on your email.",
"3016": "Send otp on your Email successfully"
"3016": "Send otp on your Email successfully",
"3017": "Profile image update successfully"
}
STATUS_CODE_ERROR = {

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0002_remove_guardian_junior_code'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-28 06:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0003_guardian_country_name'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0004_guardian_image'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0005_alter_guardian_image'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_verified',
field=models.BooleanField(default=False),
),
]

View File

@ -13,18 +13,22 @@ class Guardian(models.Model):
"""Contact details"""
country_code = models.IntegerField(blank=True, null=True)
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
country_name = models.CharField(max_length=30, null=True, blank=True, default=None)
"""Image info"""
image = models.ImageField(null=True, blank=True, default=None)
"""Personal info"""
family_name = models.CharField(max_length=50, null=True, blank=True, default=None)
gender = models.CharField(choices=GENDERS, max_length=15, null=True, blank=True, default=None)
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
"""Profile activity"""
is_active = models.BooleanField(default=True)
is_verified = models.BooleanField(default=False)
is_complete_profile = models.BooleanField(default=False)
passcode = models.IntegerField(null=True, blank=True, default=None)
"""Codes"""
guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None)
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)
"""Profile activity"""
is_active = models.BooleanField(default=True)
is_complete_profile = models.BooleanField(default=False)
passcode = models.IntegerField(null=True, blank=True, default=None)
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@ -8,8 +8,10 @@ from django.db import transaction
from django.contrib.auth.models import User
"""Import Django app"""
from .models import Guardian
from account.models import UserProfile
from account.models import UserProfile, UserEmailOtp
from base.messages import ERROR_CODE, SUCCESS_CODE
from .utils import upload_image_to_alibaba
from junior.models import Junior
class UserSerializer(serializers.ModelSerializer):
"""User serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token')
@ -33,17 +35,21 @@ class UserSerializer(serializers.ModelSerializer):
"""Create user profile"""
user = User.objects.create_user(username=email, email=email, password=password)
UserProfile.objects.create(user=user, user_type=user_type)
if user_type == '1':
Junior.objects.create(auth=user)
if user_type == '2':
Guardian.objects.create(user=user)
return user
except Exception as e:
"""Error handling"""
logging.error(e)
raise serializers.ValidationError({"details":ERROR_CODE['2021']})
def save(self, **kwargs):
"""save the data"""
with transaction.atomic():
instance = super().save(**kwargs)
return instance
otp = UserEmailOtp.objects.filter(email=email).last()
otp_verified = False
if otp and otp.is_verified:
otp_verified = True
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
"code": 400, "status":"failed",
})
class CreateGuardianSerializer(serializers.ModelSerializer):
"""Create guardian serializer"""
@ -57,12 +63,14 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
family_name = serializers.CharField(max_length=100, required=False)
dob = serializers.DateField(required=False)
referral_code = serializers.CharField(max_length=100, required=False)
image = serializers.ImageField(required=False)
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code',
'dob', 'referral_code', 'passcode', 'is_complete_profile']
'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used',
'country_name', 'image']
def get_first_name(self,obj):
"""first name of guardian"""
@ -78,12 +86,18 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""Create guardian profile"""
# phone_number = validated_data.get('phone', None)
# guardian_data = Guardian.objects.filter(phone=phone_number)
# junior_data = Junior.objects.filter(phone=phone_number)
# if phone_number and (guardian_data or junior_data):
# raise serializers.ValidationError({"details": ERROR_CODE['2012']})
user = User.objects.filter(username=self.context['user']).last()
if user:
"""Save first and last name of guardian"""
user.first_name = self.context.get('first_name', user.first_name)
user.last_name = self.context.get('last_name', user.last_name)
user.save()
if self.context.get('first_name') != '' and self.context.get('last_name') != '':
user.first_name = self.context.get('first_name', user.first_name)
user.last_name = self.context.get('last_name', user.last_name)
user.save()
"""Create guardian data"""
guardian, created = Guardian.objects.get_or_create(user=self.context['user'])
if created:
@ -99,13 +113,19 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
guardian.phone = validated_data.get('phone', guardian.phone)
guardian.country_code = validated_data.get('country_code', guardian.country_code)
guardian.passcode = validated_data.get('passcode', guardian.passcode)
guardian.country_name = validated_data.get('country_name', guardian.country_name)
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([guardian.phone, guardian.gender, guardian.family_name,
complete_profile_field = all([guardian.phone, guardian.gender, guardian.family_name, guardian.country_name,
guardian.dob, guardian.country_code, user.first_name, user.last_name])
guardian.is_complete_profile = False
if complete_profile_field:
guardian.is_complete_profile = True
image = validated_data.pop('image', None)
if image:
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
guardian.image = image_url
guardian.save()
return guardian

13
guardian/utils.py Normal file
View File

@ -0,0 +1,13 @@
import oss2
from django.conf import settings
import tempfile
def upload_image_to_alibaba(image, filename):
# Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
temp_file.write(image.read())
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name)
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{filename}"

View File

@ -3,8 +3,9 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
"""Import Django app"""
from .serializers import UserSerializer
from .serializers import CreateGuardianSerializer
from .serializers import UserSerializer, CreateGuardianSerializer
from .models import Guardian
from junior.models import Junior
from account.models import UserEmailOtp
from .tasks import generate_otp
from account.utils import send_otp_email
@ -22,7 +23,7 @@ class SignupViewset(viewsets.ModelViewSet):
serializer.save()
"""Generate otp"""
otp = generate_otp()
UserEmailOtp.objects.create(email=request.data['email'], otp=otp)
UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']))
"""Send email to the register user"""
send_otp_email(request.data['email'], otp)
return custom_response(SUCCESS_CODE['3001'], {"email_otp": otp},

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='junior',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-28 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0002_junior_country_name'),
]
operations = [
migrations.AddField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0003_junior_image'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0004_alter_junior_image'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_verified',
field=models.BooleanField(default=False),
),
]

View File

@ -14,10 +14,11 @@ class Junior(models.Model):
"""Contact details"""
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
country_code = models.IntegerField(blank=True, null=True)
country_name = models.CharField(max_length=30, null=True, blank=True, default=None)
"""Personal info"""
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.ImageField(upload_to='images/')
image = models.ImageField(null=True, blank=True, default=None)
"""Codes"""
junior_code = models.CharField(max_length=10, null=True, blank=True, default=None)
guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True)
@ -27,6 +28,7 @@ class Junior(models.Model):
is_active = models.BooleanField(default=True)
is_complete_profile = models.BooleanField(default=False)
passcode = models.IntegerField(null=True, blank=True, default=None)
is_verified = models.BooleanField(default=False)
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@ -6,6 +6,9 @@ from django.db import transaction
import random
"""Import django app"""
from junior.models import Junior
from guardian.utils import upload_image_to_alibaba
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.models import Guardian
class ListCharField(serializers.ListField):
"""Serializer for Array field"""
@ -19,7 +22,7 @@ class ListCharField(serializers.ListField):
"""internal value"""
if isinstance(data, list):
return data
raise serializers.ValidationError({"details":"Invalid input. Expected a list of strings."})
raise serializers.ValidationError({"details":ERROR_CODE['2025']})
class CreateJuniorSerializer(serializers.ModelSerializer):
@ -32,12 +35,14 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
dob = serializers.DateField(required=False)
referral_code = serializers.CharField(max_length=100, required=False)
guardian_code = ListCharField(required=False)
image = serializers.ImageField(required=False)
class Meta(object):
"""Meta info"""
model = Junior
fields = ['first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code',
'passcode', 'is_complete_profile', 'guardian_code']
'passcode', 'is_complete_profile', 'guardian_code', 'referral_code_used',
'country_name', 'image']
def get_first_name(self,obj):
"""first name of junior"""
@ -53,12 +58,19 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""Create junior profile"""
image = validated_data.get('image', None)
# phone_number = validated_data.get('phone', None)
# guardian_data = Guardian.objects.filter(phone=phone_number)
# junior_data = Junior.objects.filter(phone=phone_number)
# if phone_number and (junior_data or guardian_data):
# raise serializers.ValidationError({"details":ERROR_CODE['2012']})
user = User.objects.filter(username=self.context['user']).last()
if user:
"""Save first and last name of junior"""
user.first_name = self.context.get('first_name', user.first_name)
user.last_name = self.context.get('last_name', user.last_name)
user.save()
if self.context.get('first_name') != '' and self.context.get('last_name') != '':
user.first_name = self.context.get('first_name', user.first_name)
user.last_name = self.context.get('last_name', user.last_name)
user.save()
"""Create junior data"""
junior, created = Junior.objects.get_or_create(auth=self.context['user'])
if created:
@ -72,16 +84,21 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code)
junior.dob = validated_data.get('dob',junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name)
"""Update country code and phone number"""
junior.phone = validated_data.get('phone', junior.phone)
junior.country_code = validated_data.get('country_code', junior.country_code)
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([junior.phone, junior.gender, junior.family_name,
complete_profile_field = all([junior.phone, junior.gender, junior.country_name,
junior.dob, junior.country_code, user.first_name, user.last_name])
junior.is_complete_profile = False
if complete_profile_field:
junior.is_complete_profile = True
if image:
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
junior.image = image_url
junior.save()
return junior

View File

@ -19,6 +19,6 @@ upstream web {
location /static {
autoindex on;
alias /usr/src/app/zod_bank/static/;
alias /usr/src/app/static/;
}
}

View File

@ -1,19 +1,25 @@
aliyun-python-sdk-core==2.13.36
aliyun-python-sdk-dysmsapi==2.1.2
aliyun-python-sdk-kms==2.16.1
aliyun-python-sdk-sts==3.1.1
amqp==5.1.1
asgiref==3.7.2
async-timeout==4.0.2
billiard==4.1.0
boto3==1.26.157
botocore==1.29.157
cachetools==5.3.1
celery==5.3.1
certifi==2023.5.7
cffi==1.15.1
channels==4.0.0
channels-redis==4.1.0
charset-normalizer==3.1.0
click==8.1.3
click-didyoumean==0.3.0
click-plugins==1.1.1
click-repl==0.3.0
crcmod==1.7
cron-descriptor==1.4.0
cryptography==41.0.1
decouple==0.0.7
@ -33,16 +39,23 @@ django-timezone-field==5.1
djangorestframework==3.14.0
djangorestframework-simplejwt==5.2.2
drf-yasg==1.21.6
google-auth==2.21.0
gunicorn==20.1.0
idna==3.4
inflection==0.5.1
jmespath==0.10.0
kombu==5.3.1
msgpack==1.0.5
oss2==2.18.0
packaging==23.1
phonenumbers==8.13.15
Pillow==9.5.0
prompt-toolkit==3.0.38
psycopg==3.1.9
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycparser==2.21
pycryptodome==3.18.0
PyJWT==2.7.0
python-crontab==2.7.1
python-dateutil==2.8.2
@ -50,6 +63,8 @@ python-dotenv==1.0.0
pytz==2023.3
PyYAML==6.0
redis==4.5.5
requests==2.31.0
rsa==4.9
s3transfer==0.6.1
six==1.16.0
sqlparse==0.4.4

View File

@ -56,6 +56,7 @@ INSTALLED_APPS = [
'account',
'junior',
'guardian',
# 'social_django'
]
MIDDLEWARE = [
@ -95,7 +96,7 @@ REST_FRAMEWORK = {
]
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=50),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
}
# Database
@ -171,14 +172,49 @@ CORS_ALLOW_HEADERS = (
"""Static files (CSS, JavaScript, Images)
https://docs.djangoproject.com/en/3.0/howto/static-files/"""
# AUTHENTICATION_BACKENDS = [
# 'social_core.backends.google.GoogleOAuth2',
# 'django.contrib.auth.backends.ModelBackend',
# ]
#
# LOGIN_URL = 'login'
# LOGIN_REDIRECT_URL = 'home'
# LOGOUT_URL = 'logout'
# LOGOUT_REDIRECT_URL = 'login'
# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'infozodbank@gmail.com'
# Replace with your Gmail email password or App password
EMAIL_HOST_PASSWORD = 'ghwdmznwwslvchga'
# SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
# SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''
STATIC_URL = '/static/'
GOOGLE_CLIENT_ID = "182276566528-hlbjncs19fo502jposod6kft2p9k4grk.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET = "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"
# EMAIL_BACKEND = os.getenv('EMAIL_BACKEND')
# EMAIL_HOST = os.getenv('EMAIL_HOST')
# EMAIL_PORT = os.getenv('EMAIL_PORT')
# EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS')
# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') # Replace with your Gmail email address
# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') # Replace with your Gmail email password or App password
# EMAIL_FROM_ADDRESS = os.getenv('EMAIL_FROM_ADDRESS')
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST="smtp.sendgrid.net"
EMAIL_PORT="587"
EMAIL_USE_TLS="True"
EMAIL_HOST_USER="apikey" # Replace with your Gmail email address
EMAIL_HOST_PASSWORD="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"
EMAIL_FROM_ADDRESS="zodbank@yopmail.com"
# ALIYUN_OSS_ACCESS_KEY_ID = os.getenv('ALIYUN_OSS_ACCESS_KEY_ID')
# ALIYUN_OSS_ACCESS_KEY_SECRET = os.getenv('ALIYUN_OSS_ACCESS_KEY_SECRET')
# ALIYUN_OSS_BUCKET_NAME = os.getenv('ALIYUN_OSS_BUCKET_NAME')
# ALIYUN_OSS_ENDPOINT = os.getenv('ALIYUN_OSS_ENDPOINT')
# ALIYUN_OSS_REGION = os.getenv('ALIYUN_OSS_REGION')
ALIYUN_OSS_ACCESS_KEY_ID="LTAI5t7w1gq1CswJtvxtEZTd"
ALIYUN_OSS_ACCESS_KEY_SECRET="6yknAFpP2gVMhCWAJwbAjCEw2eehpf"
ALIYUN_OSS_BUCKET_NAME="zod-dev"
ALIYUN_OSS_ENDPOINT="oss-me-central-1.aliyuncs.com"
ALIYUN_OSS_REGION="Global"
STATIC_URL = 'static/'
STATIC_ROOT = 'static'