diff --git a/account/serializers.py b/account/serializers.py index afd923c..5f03fc2 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -12,6 +12,7 @@ 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 class GoogleSignInSerializer(serializers.Serializer): """Google login Serializer""" @@ -20,7 +21,6 @@ class GoogleSignInSerializer(serializers.Serializer): def create(self, validated_data): """Create or update user model""" with transaction.atomic(): - print("validated_data====>",validated_data) if User.objects.filter(email__iexact=self.validated_data['email']).exists(): return User.objects.get(email__iexact=self.validated_data['email']) @@ -29,7 +29,34 @@ class GoogleSignInSerializer(serializers.Serializer): email=self.validated_data['email']) return instance +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) @@ -41,15 +68,10 @@ 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) user_details.set_password(password) user_details.save() return {'password':password} @@ -69,13 +91,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} diff --git a/account/urls.py b/account/urls.py index eab8be4..b9501e6 100644 --- a/account/urls.py +++ b/account/urls.py @@ -5,7 +5,7 @@ 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) """Router""" router = routers.SimpleRouter() @@ -17,10 +17,10 @@ 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()) ] diff --git a/account/utils.py b/account/utils.py index f20ef19..bfc2399 100644 --- a/account/utils.py +++ b/account/utils.py @@ -6,7 +6,7 @@ from rest_framework.response import Response from templated_email import send_templated_mail 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', diff --git a/account/views.py b/account/views.py index 69e4e95..7ed3918 100644 --- a/account/views.py +++ b/account/views.py @@ -9,7 +9,7 @@ from account.models import UserProfile, UserPhoneOtp, UserEmailOtp from django.contrib.auth.models import User from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer, ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer, - GoogleSignInSerializer) + GoogleSignInSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer) from rest_framework_simplejwt.tokens import RefreshToken from base.messages import ERROR_CODE, SUCCESS_CODE from guardian.tasks import generate_otp @@ -44,7 +44,19 @@ from templated_email import send_templated_mail # return custom_response(SUCCESS_CODE["3003"], response_status=status.HTTP_200_OK) # return custom_response(ERROR_CODE["2002"], 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] diff --git a/base/messages.py b/base/messages.py index 37507fd..f977ee7 100644 --- a/base/messages.py +++ b/base/messages.py @@ -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.", @@ -47,7 +47,9 @@ ERROR_CODE = { "2021": "Already register", "2022":"Invalid Guardian code", "2023":"Invalid user", - "2024":"Email not verified" + "2024":"Email not verified", + "2025":"Invalid input. Expected a list of strings.", + "2026" : "New password should not same as old password" } SUCCESS_CODE = { # Success code for password @@ -75,7 +77,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 = { diff --git a/guardian/migrations/0004_guardian_image.py b/guardian/migrations/0004_guardian_image.py new file mode 100644 index 0000000..519ed02 --- /dev/null +++ b/guardian/migrations/0004_guardian_image.py @@ -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/'), + ), + ] diff --git a/guardian/models.py b/guardian/models.py index d9228b7..0512ee4 100644 --- a/guardian/models.py +++ b/guardian/models.py @@ -14,6 +14,8 @@ class Guardian(models.Model): 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(upload_to='images/', 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) diff --git a/guardian/serializers.py b/guardian/serializers.py index f2c0c46..1be4638 100644 --- a/guardian/serializers.py +++ b/guardian/serializers.py @@ -10,6 +10,8 @@ from django.contrib.auth.models import User from .models import Guardian from account.models import UserProfile 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') @@ -57,13 +59,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', 'referral_code_used', - 'country_name'] + 'country_name', 'image'] def get_first_name(self,obj): """first name of guardian""" @@ -79,6 +82,11 @@ class CreateGuardianSerializer(serializers.ModelSerializer): def create(self, validated_data): """Create guardian profile""" + phone_number = validated_data.pop('phone', None) + guardian_data = Guardian.objects.filter(phone=phone_number) + junior_data = Junior.objects.filter(phone=phone_number) + if 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""" @@ -109,6 +117,11 @@ class CreateGuardianSerializer(serializers.ModelSerializer): 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 diff --git a/guardian/utils.py b/guardian/utils.py new file mode 100644 index 0000000..0e46c6c --- /dev/null +++ b/guardian/utils.py @@ -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}" diff --git a/images/Screenshot_from_2023-06-28_14-22-20.png b/images/Screenshot_from_2023-06-28_14-22-20.png new file mode 100644 index 0000000..217186a Binary files /dev/null and b/images/Screenshot_from_2023-06-28_14-22-20.png differ diff --git a/images/Screenshot_from_2023-06-28_15-16-09.png b/images/Screenshot_from_2023-06-28_15-16-09.png new file mode 100644 index 0000000..e79b16a Binary files /dev/null and b/images/Screenshot_from_2023-06-28_15-16-09.png differ diff --git a/images/Screenshot_from_2023-06-28_15-16-09_MkRvErJ.png b/images/Screenshot_from_2023-06-28_15-16-09_MkRvErJ.png new file mode 100644 index 0000000..e79b16a Binary files /dev/null and b/images/Screenshot_from_2023-06-28_15-16-09_MkRvErJ.png differ diff --git a/images/Screenshot_from_2023-06-28_15-16-09_o1it3PP.png b/images/Screenshot_from_2023-06-28_15-16-09_o1it3PP.png new file mode 100644 index 0000000..e79b16a Binary files /dev/null and b/images/Screenshot_from_2023-06-28_15-16-09_o1it3PP.png differ diff --git a/junior/migrations/0003_junior_image.py b/junior/migrations/0003_junior_image.py new file mode 100644 index 0000000..8a31d3f --- /dev/null +++ b/junior/migrations/0003_junior_image.py @@ -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/'), + ), + ] diff --git a/junior/models.py b/junior/models.py index b389e3a..8d5660d 100644 --- a/junior/models.py +++ b/junior/models.py @@ -18,7 +18,7 @@ class Junior(models.Model): """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(upload_to='images/', 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) diff --git a/junior/serializers.py b/junior/serializers.py index a741203..2128392 100644 --- a/junior/serializers.py +++ b/junior/serializers.py @@ -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,13 +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', 'referral_code_used', - 'country_name'] + 'country_name', 'image'] def get_first_name(self,obj): """first name of junior""" @@ -54,6 +58,12 @@ class CreateJuniorSerializer(serializers.ModelSerializer): def create(self, validated_data): """Create junior profile""" + image = validated_data.pop('image', None) + phone_number = validated_data.pop('phone', None) + guardian_data = Guardian.objects.filter(phone=phone_number) + junior_data = Junior.objects.filter(phone=phone_number) + if 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""" @@ -85,6 +95,10 @@ class CreateJuniorSerializer(serializers.ModelSerializer): 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 diff --git a/zod_bank/settings.py b/zod_bank/settings.py index 1c758b7..763cf47 100644 --- a/zod_bank/settings.py +++ b/zod_bank/settings.py @@ -172,20 +172,29 @@ CORS_ALLOW_HEADERS = ( https://docs.djangoproject.com/en/3.0/howto/static-files/""" -# 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' +# Email settings For temporary use +# 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' + +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') + +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') -# EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS') -# EMAIL_HOST = os.getenv('EMAIL_HOST') -# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') -# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') -# EMAIL_PORT = os.getenv('EMAIL_PORT') STATIC_URL = 'static/' STATIC_ROOT = 'static'