Skip to content

Commit

Permalink
Merge pull request #2234 from gtech-mulearn/dev
Browse files Browse the repository at this point in the history
Dev Server
  • Loading branch information
nashnsulthan authored Oct 6, 2024
2 parents f407f41 + bb9368b commit 756fe2c
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 15 deletions.
7 changes: 6 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ PROTECTED_API_KEY =

SYSTEM_ADMIN_ID =

LAUNCHPAD_ADMIN_EMAIL =
LAUNCHPAD_ADMIN_EMAIL =

DISCORD_CLIENT_ID=xxx
DISCORD_CLIENT_SECRET=xxx
DISCORD_GUILD_ID=xxx
DISCORD_BOT_TOKEN=xxx
99 changes: 97 additions & 2 deletions api/register/register_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,110 @@
from db.organization import Country, Department, District, Organization, State, Zone
from django.utils.decorators import method_decorator
from db.task import InterestGroup
from db.user import Role, User
from db.user import Role, User, UserInterests
from utils.response import CustomResponse
from utils.types import OrganizationType
from utils.utils import send_template_mail
from . import serializers
from .register_helper import get_auth_token
from django.views.decorators.cache import cache_page
from django.core.cache import cache
from mu_celery.task import send_email
from utils.permission import CustomizePermission, JWTUtils
from decouple import config
import requests
from mu_celery.task import onboard_user

DISCORD_CLIENT_ID = config("DISCORD_CLIENT_ID")
DISCORD_CLIENT_SECRET = config("DISCORD_CLIENT_SECRET")
FR_DOMAIN_NAME = config("FR_DOMAIN_NAME")


class ConnectDiscordAPI(APIView):
def get(self, request):
if not JWTUtils.is_jwt_authenticated(request):
return CustomResponse(
general_message="Unauthorized access"
).get_failure_response()
user_id = JWTUtils.fetch_user_id(request)
token = request.GET.get("code")
if not token:
return CustomResponse(
general_message="Invalid or no token given"
).get_failure_response()
token_url = "https://discord.com/api/oauth2/token"
redirect_uri = f"{FR_DOMAIN_NAME}/dashboard/connect-discord/"
data = {
"client_id": DISCORD_CLIENT_ID,
"client_secret": DISCORD_CLIENT_SECRET,
"grant_type": "authorization_code",
"code": token,
"redirect_uri": redirect_uri,
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
token_response = requests.post(
token_url,
data=data,
headers=headers,
)
access_token = token_response.json().get("access_token")
if token_response.status_code != 200:
return CustomResponse(
general_message="Failed to get access token"
).get_failure_response()
onboard_user.delay(access_token, user_id)
return CustomResponse(
general_message="You will be added to the discord server soon"
).get_success_response()


class UserInterestAPI(APIView):
permission_classes = [CustomizePermission]

def put(self, request):
if not JWTUtils.is_jwt_authenticated(request):
return CustomResponse(
general_message="Unauthorized access"
).get_failure_response()
user_id = JWTUtils.fetch_user_id(request)
if not (user := cache.get(f"db_user_{user_id}")):
user = User.objects.filter(id=user_id).first()
user_interest = UserInterests.objects.filter(user=user).first()
if not user_interest:
return CustomResponse(
general_message="User interests not found"
).get_failure_response()
serializer = serializers.UserInterestSerializer(
instance=user_interest, data=request.data, context={"user": user}
)
if serializer.is_valid():
serializer.update(user_interest, serializer.validated_data)
return CustomResponse(
general_message="Updated interests"
).get_success_response()
return CustomResponse(general_message=serializer.errors).get_failure_response()

def post(self, request):
if not JWTUtils.is_jwt_authenticated(request):
return CustomResponse(
general_message="Unauthorized access"
).get_failure_response()
user_id = JWTUtils.fetch_user_id(request)
if not (user := cache.get(f"db_user_{user_id}")):
user = User.objects.filter(id=user_id).first()
user_interest = UserInterests.objects.filter(user=user).first()
if user_interest:
return CustomResponse(
general_message="User interests already exist"
).get_failure_response()
serializer = serializers.UserInterestSerializer(
data=request.data, context={"user": user}
)
if serializer.is_valid():
serializer.save()
return CustomResponse(
general_message="Added interests"
).get_success_response()
return CustomResponse(general_message=serializer.errors).get_failure_response()


class UserRegisterValidateAPI(APIView):
Expand Down
93 changes: 89 additions & 4 deletions api/register/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,24 @@
UserOrganizationLink,
Zone,
)
from db.task import InterestGroup, Level, MucoinInviteLog, UserIgLink, UserLvlLink, Wallet
from db.user import Role, Socials, User, UserMentor, UserReferralLink, UserRoleLink, UserSettings
from db.task import (
InterestGroup,
Level,
MucoinInviteLog,
UserIgLink,
UserLvlLink,
Wallet,
)
from db.user import (
Role,
Socials,
User,
UserMentor,
UserReferralLink,
UserRoleLink,
UserSettings,
UserInterests,
)
from utils.exception import CustomException
from utils.types import OrganizationType, RoleType
from utils.utils import DateTimeUtils
Expand Down Expand Up @@ -163,7 +179,6 @@ class Meta:
]



class ReferralSerializer(serializers.ModelSerializer):
user = serializers.CharField(required=False)
muid = serializers.CharField(required=False)
Expand Down Expand Up @@ -326,6 +341,7 @@ class Meta:
"area_of_interest",
]


class RegisterSerializer(serializers.Serializer):
user = UserSerializer()
organization = UserOrgLinkSerializer(required=False)
Expand All @@ -351,7 +367,7 @@ def create(self, validated_data):

if mentor := validated_data.pop("mentor", None):
mentor["user"] = user
MentorSerializer().create(mentor)
MentorSerializer().create(mentor)

return user

Expand Down Expand Up @@ -399,3 +415,72 @@ class Meta:

def get_location(self, obj):
return f"{obj.name}, {obj.zone.state.name}, {obj.zone.state.country.name}"


class UserInterestSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True)
user = serializers.CharField(read_only=True)
choosen_interests = serializers.JSONField()
other_interests = serializers.JSONField(required=False)
choosen_endgoals = serializers.JSONField()
other_endgoals = serializers.JSONField(required=False)
created_at = serializers.DateTimeField(read_only=True)
updated_at = serializers.DateTimeField(read_only=True)

def create(self, validated_data):
validated_data["created_at"] = validated_data["updated_at"] = (
DateTimeUtils.get_current_utc_time()
)
if user := self.context.get("user"):
validated_data["user"] = user
else:
return serializers.ValidationError("User not found")
return super().create(validated_data)

def update(self, instance, validated_data):
if validated_data.get("choosen_interests", None):
instance.choosen_interests = validated_data.get("choosen_interests", [])
if validated_data.get("other_interests", None):
instance.other_interests = validated_data.get("other_interests", [])
if validated_data.get("choosen_endgoals", None):
instance.choosen_endgoals = validated_data.get("choosen_endgoals", [])
if validated_data.get("other_endgoals", None):
instance.other_endgoals = validated_data.get("other_endgoals", [])
instance.updated_at = DateTimeUtils.get_current_utc_time()
return instance.save()

def validate_choosen_interests(self, interests):
if not all(
interest in ("maker", "software", "creative", "management", "others")
for interest in interests
):
raise serializers.ValidationError("Invalid interests selected.")
return list(set(interests))

def validate_choosen_endgoals(self, end_goals):
if not all(
goal
in (
"job",
"higher_education",
"gig_work",
"entrepreneurship",
"others",
)
for goal in end_goals
):
raise serializers.ValidationError("Invalid end goals selected.")
return list(set(end_goals))

class Meta:
model = UserInterests
fields = [
"id",
"user",
"choosen_interests",
"other_interests",
"choosen_endgoals",
"other_endgoals",
"created_at",
"updated_at",
]
15 changes: 7 additions & 8 deletions api/register/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
path("role/list/", register_views.RoleAPI.as_view()),
path("colleges/", register_views.CollegesAPI.as_view()),
path("department/list/", register_views.DepartmentAPI.as_view()),

path("location/", register_views.LocationSearchView.as_view()),

path("country/list/", register_views.CountryAPI.as_view()),
path("state/list/", register_views.StateAPI.as_view()),
path("district/list/", register_views.DistrictAPI.as_view()),
Expand All @@ -20,9 +18,10 @@
path("schools/list/", register_views.SchoolAPI.as_view()),
path("area-of-interest/list/", register_views.AreaOfInterestAPI.as_view()),
path("lc/user-validation/", register_views.LearningCircleUserViewAPI.as_view()),
path('email-verification/', register_views.UserEmailVerificationAPI.as_view()),
path('user-country/', register_views.UserCountryAPI.as_view()),
path('user-state/', register_views.UserStateAPI.as_view()),
path('user-zone/', register_views.UserZoneAPI.as_view()),

]
path("email-verification/", register_views.UserEmailVerificationAPI.as_view()),
path("user-country/", register_views.UserCountryAPI.as_view()),
path("user-state/", register_views.UserStateAPI.as_view()),
path("user-zone/", register_views.UserZoneAPI.as_view()),
path("interests/", register_views.UserInterestAPI.as_view()),
path("connect-discord/", register_views.ConnectDiscordAPI.as_view()),
]
14 changes: 14 additions & 0 deletions db/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.conf import settings

from .managers import user_manager

# from .task import UserIgLink
from decouple import config as decouple_config

Expand Down Expand Up @@ -56,6 +57,19 @@ def save(self, *args, **kwargs):

return super().save(*args, **kwargs)

class UserInterests(models.Model):
id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)
user = models.ForeignKey(User, on_delete=models.CASCADE)
choosen_interests = models.JSONField(max_length=100)
other_interests = models.JSONField(max_length=100, blank=True, null=True)
choosen_endgoals = models.JSONField(max_length=100)
other_endgoals = models.JSONField(max_length=100, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
managed = False
db_table = 'user_interests'

class UserMentor(models.Model):
id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)
Expand Down
43 changes: 43 additions & 0 deletions mu_celery/task.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
from celery import shared_task
from utils.utils import send_template_mail
import requests
from decouple import config
from db.user import User

DISCORD_GUILD_ID = config("DISCORD_GUILD_ID")
DISCORD_BOT_TOKEN = config("DISCORD_BOT_TOKEN")


@shared_task
def send_email(context: dict, subject: str, address: list[str], attachment: str = None):
return send_template_mail(context, subject, address, attachment)


@shared_task
def onboard_user(access_token: str, user_id: int):
user = User.objects.get(id=user_id)
user_response = requests.get(
"https://discord.com/api/users/@me",
headers={"Authorization": f"Bearer {access_token}"},
)
if user_response.status_code != 200:
return {"status": "error", "message": "Failed to get user data"}
user_data = user_response.json()
discord_user_id = user_data.get("id")
guild_url = (
f"https://discord.com/api/guilds/{DISCORD_GUILD_ID}/members/{discord_user_id}"
)
member_data = {"access_token": access_token}
bot_headers = {
"Authorization": f"Bot {DISCORD_BOT_TOKEN}",
"Content-Type": "application/json",
}
already_linked_account = User.objects.filter(discord_id=discord_user_id).first()
if already_linked_account:
already_linked_account.exist_in_guild = False
already_linked_account.discord_id = None
already_linked_account.save()
user.discord_id = discord_user_id
user.exist_in_guild = True
user.save()
join_response = requests.put(guild_url, json=member_data, headers=bot_headers)
if (
join_response.status_code != 201
and join_response.status_code != 200
and join_response.status_code != 204
):
return {"status": "error", "message": "Failed to join guild"}
return {"status": "success", "message": "User onboarded successfully"}

0 comments on commit 756fe2c

Please sign in to comment.