diff --git a/src/api/marketplace/accounts/migrations/0009_accountlanguage.py b/src/api/marketplace/accounts/migrations/0009_accountlanguage.py new file mode 100644 index 00000000..9e0cec31 --- /dev/null +++ b/src/api/marketplace/accounts/migrations/0009_accountlanguage.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.7 on 2024-01-02 11:54 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_languagemaster_delete_language'), + ('accounts', '0008_rename_walletproider_walletprovider'), + ] + + operations = [ + migrations.CreateModel( + name='AccountLanguage', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Account Language ID')), + ('language', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='acc_language_master_id', to='core.languagemaster')), + ('user_account', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='acc_user_account_id', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'account_language', + }, + ), + ] diff --git a/src/api/marketplace/accounts/models.py b/src/api/marketplace/accounts/models.py index 28023238..7f17aad9 100644 --- a/src/api/marketplace/accounts/models.py +++ b/src/api/marketplace/accounts/models.py @@ -3,7 +3,7 @@ from django.db import models from django.contrib.auth.models import AbstractUser from django.db.models import SET_NULL -from core.models import Currency +from core.models import Currency, LanguageMaster from core.models import Country import uuid @@ -49,7 +49,7 @@ class AccountCategory(models.Model): null=True) class Meta: - db_table = "account_category" + db_table = "account_category" class Role(models.Model): @@ -97,6 +97,18 @@ class Meta: def __str__(self): return self.username + +class AccountLanguage(models.Model): + id = models.UUIDField(primary_key=True, verbose_name='Account Language ID', default=uuid.uuid4, editable=False) + user_account = models.ForeignKey(User, related_name='acc_user_account_id', on_delete=SET_NULL, + null=True) + language = models.ForeignKey(LanguageMaster, related_name='acc_language_master_id', on_delete=SET_NULL, + null=True) + + class Meta: + db_table = "account_language" + + class BankAccount(models.Model): id = models.UUIDField( diff --git a/src/api/marketplace/accounts/serializers.py b/src/api/marketplace/accounts/serializers.py index 8433f0ab..537bfdf6 100644 --- a/src/api/marketplace/accounts/serializers.py +++ b/src/api/marketplace/accounts/serializers.py @@ -1,7 +1,9 @@ from unicodedata import category from rest_framework import serializers from uuid import UUID -from .models import TwitterAccount, CategoryMaster, AccountCategory, User, BankAccount, Role + +from core.serializers import LanguageMasterSerializer +from .models import AccountLanguage, TwitterAccount, CategoryMaster, AccountCategory, User, BankAccount, Role class CategoryMasterSerializer(serializers.ModelSerializer): class Meta: @@ -60,9 +62,17 @@ class Meta: fields = "__all__" +class AccountLanguageSerializer(serializers.ModelSerializer): + language = LanguageMasterSerializer(read_only=True) + + class Meta: + model = AccountLanguage + fields = "__all__" + class UserSerializer(serializers.ModelSerializer): twitter_account = TwitterAccountSerializer(read_only=True) role = RoleSerializer(read_only=True) + account_languages = AccountLanguageSerializer(many=True, read_only=True, source='acc_user_account_id') class Meta: model = User diff --git a/src/api/marketplace/accounts/views.py b/src/api/marketplace/accounts/views.py index b816f098..1f663563 100644 --- a/src/api/marketplace/accounts/views.py +++ b/src/api/marketplace/accounts/views.py @@ -1,6 +1,9 @@ +from distutils.util import strtobool from http.client import HTTPResponse +import uuid from urllib import request from marketplace.authentication import JWTAuthentication +from django.db.models import Q from marketplace.services import ( EmailService, Pagination, @@ -8,14 +11,26 @@ handleBadRequest, handleNotFound, handleDeleteNotAllowed, - JWTOperations + JWTOperations, ) from drf_yasg.utils import swagger_auto_schema from django.core.exceptions import ValidationError from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status -from .models import TwitterAccount, CategoryMaster, AccountCategory, User, BankAccount, Role, Wallet, WalletNetwork, WalletProvider +from packages.models import Package, Service +from .models import ( + AccountLanguage, + TwitterAccount, + CategoryMaster, + AccountCategory, + User, + BankAccount, + Role, + Wallet, + WalletNetwork, + WalletProvider, +) from .serializers import ( CreateAccountCategorySerializer, DeleteAccountCategorySerializer, @@ -30,17 +45,17 @@ TwitterAuthSerializer, RoleSerializer, EmailVerificationSerializer, - WalletAuthSerializer + WalletAuthSerializer, ) from .services import OTPAuthenticationService, TwitterAuthenticationService from decouple import config import datetime - # Twitter account API-Endpoint # List-Create-API + class RoleList(APIView): def get(self, request): try: @@ -84,10 +99,127 @@ def get(self, request, pk): except Exception as e: return handleServerException(e) + class TwitterAccountList(APIView): def get(self, request): try: + languages = request.GET.getlist("languages", []) + serviceTypes = request.GET.getlist("serviceTypes", []) + categories = request.GET.getlist("categories", []) + + upperPriceLimit = request.GET.get("upperPriceLimit", None) + lowerPriceLimit = request.GET.get("lowerPriceLimit", None) + upperFollowerLimit = request.GET.get("upperFollowerLimit", None) + lowerFollowerLimit = request.GET.get("lowerFollowerLimit", None) + + searchString = request.GET.get("searchString", "") + isVerified_str = request.GET.get("isVerified", "false") + isVerified = bool(strtobool(isVerified_str)) + + # Filter based on parameters twitterAccount = TwitterAccount.objects.all() + + # From the account model itself. + if upperFollowerLimit is not None: + twitterAccount = twitterAccount.filter( + followers_count__lte=upperFollowerLimit + ) + + if lowerFollowerLimit is not None: + twitterAccount = twitterAccount.filter( + followers_count__gte=lowerFollowerLimit + ) + + if searchString: + twitterAccount = twitterAccount.filter( + Q(user_name__icontains=searchString) + | Q(name__icontains=searchString) + ) + + if isVerified: + twitterAccount = twitterAccount.filter(verified=isVerified) + + if categories: + for category in categories: + twitterAccount = twitterAccount.filter( + cat_twitter_account_id__category__name=category + ) + + if languages: + twitter_accounts_to_exclude = [ + twitter_account.id + for twitter_account in twitterAccount + if not User.objects.filter(twitter_account=twitter_account).exists() + or not AccountLanguage.objects.filter( + user_account__twitter_account=twitter_account, + language__langEnglishName__in=languages, + ).exists() + ] + + # Exclude undesired twitter accounts from the queryset + twitterAccount = twitterAccount.exclude( + id__in=twitter_accounts_to_exclude + ) + + if serviceTypes: + twitter_accounts_to_exclude = [ + twitter_account.id + for twitter_account in twitterAccount + if not User.objects.filter(twitter_account=twitter_account).exists() + or not Package.objects.filter( + influencer__twitter_account=twitter_account + ).exists() + or not Service.objects.filter( + package__influencer__twitter_account=twitter_account, + service_master__name__in=serviceTypes, + ).exists() + ] + + # Exclude undesired twitter accounts from the queryset + twitterAccount = twitterAccount.exclude( + id__in=twitter_accounts_to_exclude + ) + + if upperPriceLimit: + twitter_accounts_to_exclude = [ + twitter_account.id + for twitter_account in twitterAccount + if not User.objects.filter(twitter_account=twitter_account).exists() + or not Package.objects.filter( + influencer__twitter_account=twitter_account + ).exists() + or not Service.objects.filter( + package__influencer__twitter_account=twitter_account, + price__lte=upperPriceLimit + ).exists() + ] + + # Exclude undesired twitter accounts from the queryset + twitterAccount = twitterAccount.exclude( + id__in=twitter_accounts_to_exclude + ) + + if lowerPriceLimit: + twitter_accounts_to_exclude = [ + twitter_account.id + for twitter_account in twitterAccount + if not User.objects.filter(twitter_account=twitter_account).exists() + or not Package.objects.filter( + influencer__twitter_account=twitter_account + ).exists() + or not Service.objects.filter( + package__influencer__twitter_account=twitter_account, + price__gte=lowerPriceLimit + ).exists() + ] + + # Exclude undesired twitter accounts from the queryset + twitterAccount = twitterAccount.exclude( + id__in=twitter_accounts_to_exclude + ) + + + # Paginate the results pagination = Pagination(twitterAccount, request) serializer = TwitterAccountSerializer(pagination.getData(), many=True) return Response( @@ -312,6 +444,7 @@ def get_object(self, pk): return None authentication_classes = [JWTAuthentication] + def get(self, request): try: # If no twitter_account_id is provided, try for the logged in user @@ -338,11 +471,13 @@ def get(self, request): return handleServerException(e) authentication_classes = [JWTAuthentication] + @swagger_auto_schema(request_body=AccountCategorySerializer) def post(self, request): try: - serializer = CreateAccountCategorySerializer(data=request.data, - context={'request': request}) + serializer = CreateAccountCategorySerializer( + data=request.data, context={"request": request} + ) if serializer.is_valid(): serializer.save() @@ -366,7 +501,7 @@ def delete(self, request): try: serializer = DeleteAccountCategorySerializer(data=request.data) if serializer.is_valid(): - account_category_ids = serializer.validated_data['account_category_ids'] + account_category_ids = serializer.validated_data["account_category_ids"] for account_category_id in account_category_ids: account_category = self.get_object(account_category_id) if account_category is None: @@ -676,7 +811,6 @@ def delete(self, request, pk): class TwitterAuth(APIView): - def get_object(self, twitter_id): try: return TwitterAccount.objects.get(twitter_id=twitter_id) @@ -689,8 +823,11 @@ def get(self, request): user_data = twitter_auth_service.get_twitter_client_data(request) twitter_account = self.get_object(user_data.id) if twitter_account: - twitter_account.access_token = twitter_auth_service.get_twitter_access_token( - request.build_absolute_uri()) + twitter_account.access_token = ( + twitter_auth_service.get_twitter_access_token( + request.build_absolute_uri() + ) + ) twitter_account.save() else: @@ -699,7 +836,8 @@ def get(self, request): name=user_data.name, user_name=user_data.username, access_token=twitter_auth_service.get_twitter_access_token( - request.build_absolute_uri()), + request.build_absolute_uri() + ), description=user_data.description, profile_image_url=user_data.profile_image_url, followers_count=user_data.public_metrics["followers_count"], @@ -727,13 +865,14 @@ def get(self, request): # payload=twitter_auth_service.get_jwt_payload(twitter_account)) response = HTTPResponse() - response['Location'] = config('FRONT_END_URL') + response["Location"] = config("FRONT_END_URL") response.status_code = 302 # response.set_cookie('jwt', JWTOperations().generateJwtToken( # twitter_auth_service.get_jwt_payload(twitter_account))) return response except Exception as e: return handleServerException(e) + @swagger_auto_schema(request_body=TwitterAuthSerializer) def post(self, request): try: @@ -786,7 +925,6 @@ def get(self, request): class OTPAuth(APIView): - def get_or_create_user(self, email): try: return User.objects.get(email=email) @@ -798,6 +936,7 @@ def get_or_create_user(self, email): ) user.save() return user + @swagger_auto_schema(request_body=OTPAuthenticationSerializer) def post(self, request): try: @@ -878,13 +1017,21 @@ def post(self, request): user_id = str(user.id) payload = { "id": user_id, - "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=86400), + "exp": datetime.datetime.utcnow() + + datetime.timedelta(seconds=86400), "iat": datetime.datetime.utcnow(), } response = Response() # Create a new Response instance token = jwt_operations.generateToken(payload) response.set_cookie( - 'jwt', token, max_age=86400, path="/", secure=True, httponly=True, samesite="None") + "jwt", + token, + max_age=86400, + path="/", + secure=True, + httponly=True, + samesite="None", + ) response.data = { "isSuccess": True, "data": UserSerializer(user).data, @@ -1020,7 +1167,6 @@ def post(self, request): class WalletAuth(APIView): - def get_wallet(self, wallet_address_id): try: return Wallet.objects.get(wallet_address_id=wallet_address_id) @@ -1030,7 +1176,10 @@ def get_wallet(self, wallet_address_id): def create_wallet(self, wallet_address_id, wallet_provider, wallet_network): try: wallet = Wallet.objects.create( - wallet_address_id=wallet_address_id, wallet_provider_id=wallet_provider, wallet_network_id=wallet_network) + wallet_address_id=wallet_address_id, + wallet_provider_id=wallet_provider, + wallet_network_id=wallet_network, + ) wallet.save() return wallet except Exception as e: @@ -1068,9 +1217,11 @@ def post(self, request): wallet = self.get_wallet(request.data["wallet_address_id"]) if wallet is None: wallet_provider = self.get_wallet_provider_id( - serializer.validated_data["wallet_provider_id"]) + serializer.validated_data["wallet_provider_id"] + ) wallet_network = self.get_wallet_network_id( - serializer.validated_data["wallet_network_id"]) + serializer.validated_data["wallet_network_id"] + ) wallet = self.create_wallet( serializer.validated_data["wallet_address_id"], wallet_provider, @@ -1080,8 +1231,7 @@ def post(self, request): if wallet.user_id is None: user = self.create_user( - serializer.validated_data["wallet_address_id"], - "business_owner" + serializer.validated_data["wallet_address_id"], "business_owner" ) wallet.user_id = user wallet.save() @@ -1093,7 +1243,8 @@ def post(self, request): user_id = str(user.id) payload = { "id": user_id, - "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=86400), + "exp": datetime.datetime.utcnow() + + datetime.timedelta(seconds=86400), "iat": datetime.datetime.utcnow(), } response = Response() @@ -1101,7 +1252,14 @@ def post(self, request): user.jwt = token user.save() response.set_cookie( - 'jwt', token, max_age=86400, path="/", secure=True, httponly=True, samesite="None") + "jwt", + token, + max_age=86400, + path="/", + secure=True, + httponly=True, + samesite="None", + ) response.data = { "isSuccess": True, "data": UserSerializer(user).data, diff --git a/src/api/marketplace/core/admin.py b/src/api/marketplace/core/admin.py index 92c04eba..f17bb50b 100644 --- a/src/api/marketplace/core/admin.py +++ b/src/api/marketplace/core/admin.py @@ -1,8 +1,8 @@ from django.contrib import admin # Register your models here. -from .models import Currency, Country, Language +from .models import Currency, Country, LanguageMaster admin.site.register(Currency) admin.site.register(Country) -admin.site.register(Language) +admin.site.register(LanguageMaster) diff --git a/src/api/marketplace/core/migrations/0003_languagemaster_delete_language.py b/src/api/marketplace/core/migrations/0003_languagemaster_delete_language.py new file mode 100644 index 00000000..a792e9c2 --- /dev/null +++ b/src/api/marketplace/core/migrations/0003_languagemaster_delete_language.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.7 on 2024-01-02 11:54 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_language'), + ] + + operations = [ + migrations.CreateModel( + name='LanguageMaster', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='Language Master')), + ('langCode', models.CharField(blank=True, max_length=100, null=True)), + ('langEnglishName', models.CharField(blank=True, max_length=100, null=True)), + ('langNativeName', models.CharField(blank=True, max_length=100, null=True)), + ], + options={ + 'db_table': 'language_master', + }, + ), + migrations.DeleteModel( + name='Language', + ), + ] diff --git a/src/api/marketplace/core/models.py b/src/api/marketplace/core/models.py index d5734cbc..fd355c6b 100644 --- a/src/api/marketplace/core/models.py +++ b/src/api/marketplace/core/models.py @@ -29,14 +29,14 @@ def __str__(self): return self.name -class Language(models.Model): - id = models.UUIDField(primary_key=True, verbose_name='Language', default=uuid.uuid4, editable=False) +class LanguageMaster(models.Model): + id = models.UUIDField(primary_key=True, verbose_name='Language Master', default=uuid.uuid4, editable=False) langCode = models.CharField(max_length=100, blank=True, null=True) langEnglishName = models.CharField(max_length=100, blank=True, null=True) langNativeName = models.CharField(max_length=100, blank=True, null=True) class Meta: - db_table = "language" + db_table = "language_master" def __str__(self): return self.langEnglishName diff --git a/src/api/marketplace/core/serializers.py b/src/api/marketplace/core/serializers.py index de7da1fa..762e8e4c 100644 --- a/src/api/marketplace/core/serializers.py +++ b/src/api/marketplace/core/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Currency, Country, Language +from .models import Currency, Country, LanguageMaster class CountrySerializer(serializers.ModelSerializer): @@ -25,7 +25,7 @@ class Meta: # fields = ('id', 'name', 'symbol', 'country') -class LanguageSerializer(serializers.ModelSerializer): +class LanguageMasterSerializer(serializers.ModelSerializer): class Meta: - model = Language + model = LanguageMaster fields = "__all__" diff --git a/src/api/marketplace/core/urls.py b/src/api/marketplace/core/urls.py index cdb346fc..56c6148a 100644 --- a/src/api/marketplace/core/urls.py +++ b/src/api/marketplace/core/urls.py @@ -13,6 +13,6 @@ path("country//", CountryDetailView.as_view()), path("currency/", CurrencyListView.as_view()), path("currency//", CurrencyDetailView.as_view()), - path("language/", LanguageListView.as_view()), - path("language//", LanguageDetailView.as_view()), + path("language-master/", LanguageListView.as_view()), + path("language-master//", LanguageDetailView.as_view()), ] diff --git a/src/api/marketplace/core/views.py b/src/api/marketplace/core/views.py index 34ddf840..efc9b34e 100644 --- a/src/api/marketplace/core/views.py +++ b/src/api/marketplace/core/views.py @@ -2,9 +2,9 @@ from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView -from .models import Country, Currency, Language +from .models import Country, Currency, LanguageMaster from django.db.models import Q -from .serializers import CountrySerializer, CurrencySerializer, LanguageSerializer +from .serializers import CountrySerializer, CurrencySerializer, LanguageMasterSerializer class CountryListView(APIView): @@ -106,17 +106,17 @@ def get(self, request, format=None): try: search = request.GET.get("search", None) order_by = request.GET.get("order_by", None) - language = Language.objects.all() + language = LanguageMaster.objects.all() if search: - language = Language.objects.filter( + language = LanguageMaster.objects.filter( Q(langCode__icontains=search) | Q(langEnglishName__icontains=search) ) else: - language = Language.objects.all() + language = LanguageMaster.objects.all() if order_by: language = language.order_by(order_by) pagination = Pagination(language, request) - serializer = LanguageSerializer(language, many=True) + serializer = LanguageMasterSerializer(language, many=True) return Response( { "isSuccess": True, @@ -133,17 +133,17 @@ def get(self, request, format=None): class LanguageDetailView(APIView): def get(self, request, pk, format=None): try: - language = Language.objects.get(pk=pk) - serializer = LanguageSerializer(language) + language = LanguageMaster.objects.get(pk=pk) + serializer = LanguageMasterSerializer(language) return Response( { "isSuccess": True, "data": serializer.data, - "message": "Language retrieved successfully", + "message": "LanguageMaster retrieved successfully", }, status=status.HTTP_200_OK, ) - except Language.DoesNotExist: + except LanguageMaster.DoesNotExist: return handleNotFound() except Exception as e: return handleServerException(e)