Skip to content

Commit

Permalink
Merge pull request #1394 from gtech-mulearn/dev
Browse files Browse the repository at this point in the history
feat: ADDED support for lc member invitation via muid
  • Loading branch information
Aashish Vinu authored Oct 22, 2023
2 parents 549b086 + d4e1378 commit 3efc1ce
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 28 deletions.
104 changes: 96 additions & 8 deletions api/dashboard/lc/dash_lc_view.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import uuid

from decouple import config
from django.core.mail import send_mail
from django.db.models import Q
from django.shortcuts import redirect
from rest_framework.views import APIView


from api.notification.notifications_utils import NotificationUtils
from db.learning_circle import LearningCircle, UserCircleLink
from db.user import User
from utils.permission import JWTUtils
from utils.response import CustomResponse
from utils.utils import send_template_mail, DateTimeUtils
from .dash_lc_serializer import LearningCircleSerializer, LearningCircleCreateSerializer, LearningCircleHomeSerializer, \
LearningCircleUpdateSerializer, LearningCircleJoinSerializer, LearningCircleMeetSerializer, \
LearningCircleMainSerializer, LearningCircleNoteSerializer, LearningCircleDataSerializer, \
Expand Down Expand Up @@ -44,11 +49,11 @@ def post(self, request, circle_code=None):
class LearningCircleCreateApi(APIView):
def post(self, request):
user_id = JWTUtils.fetch_user_id(request)
serializer = LearningCircleCreateSerializer(data=request.data, context={'user_id': user_id})
serializer = LearningCircleCreateSerializer(data=request.data, context={ 'user_id': user_id })
if serializer.is_valid():
circle = serializer.save()
return CustomResponse(general_message='LearningCircle created successfully',
response={'circle_id': circle.id}).get_success_response()
response={ 'circle_id': circle.id }).get_success_response()
return CustomResponse(message=serializer.errors).get_failure_response()


Expand All @@ -59,7 +64,7 @@ def post(self, request, circle_id):
full_name = f'{user.first_name} {user.last_name}' if user.last_name else user.first_name
lc = UserCircleLink.objects.filter(circle_id=circle_id, lead=True).first()
serializer = LearningCircleJoinSerializer(data=request.data,
context={'user_id': user_id, 'circle_id': circle_id})
context={ 'user_id': user_id, 'circle_id': circle_id })
if serializer.is_valid():
serializer.save()
user = User.objects.filter(id=lc.user.id).first()
Expand Down Expand Up @@ -113,7 +118,7 @@ class LearningCircleHomeApi(APIView):
def get(self, request, circle_id):
user_id = JWTUtils.fetch_user_id(request)
learning_circle = LearningCircle.objects.filter(id=circle_id).first()
serializer = LearningCircleHomeSerializer(learning_circle, many=False, context={"user_id": user_id})
serializer = LearningCircleHomeSerializer(learning_circle, many=False, context={ "user_id": user_id })
return CustomResponse(response=serializer.data).get_success_response()

def post(self, request, member_id, circle_id):
Expand All @@ -137,7 +142,7 @@ def patch(self, request, member_id, circle_id):
return CustomResponse(general_message='Already evaluated').get_failure_response()

serializer = LearningCircleUpdateSerializer(learning_circle_link, data=request.data,
context={'user_id': user_id})
context={ 'user_id': user_id })
if serializer.is_valid():
serializer.save()
is_accepted = request.data.get('is_accepted')
Expand Down Expand Up @@ -170,7 +175,7 @@ def delete(self, request, circle_id):
usr_circle_link = UserCircleLink.objects.filter(
circle__id=circle_id,
user__id=user_id
).first()
).first()

if not usr_circle_link:
return CustomResponse(general_message='User not part of circle').get_failure_response()
Expand All @@ -179,7 +184,7 @@ def delete(self, request, circle_id):
if (
next_lead := UserCircleLink.objects.filter(
circle__id=circle_id, accepted=1
)
)
.exclude(user__id=user_id)
.order_by('accepted_at')
.first()
Expand All @@ -194,7 +199,7 @@ def delete(self, request, circle_id):
if not UserCircleLink.objects.filter(circle__id=circle_id).exists():
if learning_circle := LearningCircle.objects.filter(
id=circle_id
).first():
).first():
learning_circle.delete()
return CustomResponse(general_message='Learning Circle Deleted').get_success_response()

Expand Down Expand Up @@ -265,5 +270,88 @@ def post(self, request):
from_mail,
[user.email],
fail_silently=False,
)
return CustomResponse(general_message='User Invited').get_success_response()


class LearningCircleInviteMember(APIView):
"""
Invite a member to a learning circle.
"""

def post(self, request, circle_id, muid):
"""
POST request to invite a member to a learning circle.
:param request: Request object.
:param circle_id: Learning circle id.
:param muid: Muid of the user.
"""
user = User.objects.filter(muid=muid).first()
if not user:
return CustomResponse(general_message='Muid is Invalid').get_failure_response()
usr_circle_link = UserCircleLink.objects.filter(circle__id=circle_id, user__id=user.id).first()
if usr_circle_link:
if usr_circle_link.accepted:
return CustomResponse(general_message='User already part of circle').get_failure_response()
elif usr_circle_link.is_invited:
return CustomResponse(general_message='User already invited').get_failure_response()
receiver_email = user.email
html_address = ["lc_invitation.html"]
inviter = User.objects.filter(id=JWTUtils.fetch_user_id(request)).first()
inviter_name = inviter.first_name + " " + inviter.last_name
context = {
"circle_name": LearningCircle.objects.filter(id=circle_id).first().name,
"inviter_name": inviter_name,
"circle_id": circle_id,
"muid": muid,
"email": receiver_email,
}
status = send_template_mail(
context=context,
subject="MuLearn - Invitation to learning circle",
address=html_address,
)
if status == 1:
usr_circle_link_new = UserCircleLink(
id=uuid.uuid4(),
circle_id=circle_id,
user=user,
is_invited=True,
accepted=False,
created_at=DateTimeUtils.get_current_utc_time(),
)
usr_circle_link_new.save()
return CustomResponse(general_message='User Invited').get_success_response()
return CustomResponse(general_message='Mail not sent').get_failure_response()


class LearningCircleInvitationStatus(APIView):
"""
API to update the invitation status
"""

def post(self, request, circle_id, muid, status):
"""
PUT request to accept the invitation to join the learning circle, by adding the user data to the lc
:param request: Request object.
:param muid: Muid of the user.
:param circle_id: Learning circle id.
:param status: Status of the invitation.
"""
user = User.objects.filter(muid=muid).first()
if not user:
return CustomResponse(general_message='Muid is Invalid').get_failure_response()
usr_circle_link = UserCircleLink.objects.filter(circle__id=circle_id, user__id=user.id).first()

if not usr_circle_link:
return CustomResponse(general_message='User not invited').get_failure_response()

if status == "accepted":
usr_circle_link.accepted = True
usr_circle_link.accepted_at = DateTimeUtils.get_current_utc_time()
usr_circle_link.save()
# return CustomResponse(general_message='User added to circle').get_success_response()
return redirect(f'{domain}/dashboard/learning-circle/')
elif status == "rejected":
usr_circle_link.delete()
return CustomResponse(general_message='User rejected invitation').get_failure_response()
4 changes: 3 additions & 1 deletion api/dashboard/lc/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
path('create/', dash_lc_view.LearningCircleCreateApi.as_view()),
path('meet/<str:circle_id>/', dash_lc_view.LearningCircleMeetAPI.as_view()),
path('join/<str:circle_id>/', dash_lc_view.LearningCircleJoinApi.as_view()),
path('member/invite/<str:circle_id>/<str:muid>/', dash_lc_view.LearningCircleInviteMember.as_view()),
path('member/invite/status/<str:circle_id>/<str:muid>/<str:status>/', dash_lc_view.LearningCircleInvitationStatus.as_view()),
path('<str:circle_id>/', dash_lc_view.LearningCircleHomeApi.as_view()),
path('<str:circle_id>/<str:member_id>/', dash_lc_view.LearningCircleHomeApi.as_view()),
path('lead/<str:circle_id>/<str:lead_id>/', dash_lc_view.LearningCircleLeadTransfer.as_view()),


]
]
28 changes: 28 additions & 0 deletions api/templates/mails/lc_invitation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{% extends "mails/base_mail.html" %} {% block content %}
<div style="background: transparent; width: 100%; height: auto">
<div style="text-align: center;">
<h1 style="
color: black;
font-family: 'Poppins';
font-weight: 600;
font-size: 30px;">
Learning Circle Invitation
</h1>
<p
>
Hey there! You have been invited to join the {{ user.circle_name }} by {{ user.inviter_name }}.
</p>
<div style="width: 320px; padding: 20px; border: 2px solid #ccc; text-align: center;">
<div style="width: 100%; height: 10px; background: transparent"></div>
<a href="{{ base_url }}/api/v1/dashboard/lc/member/invite/status/{{ user.circle_id }}/{{ user.muid }}/accepted/"
style="width: 160px; height: 40px; display: inline-block; margin: 10px;">
Accept
</a>
<a href="{{ base_url }}/api/v1/dashboard/lc/member/invite/status/{{ user.circle_id }}/{{ user.muid }}/rejected/"
style="width: 160px; height: 40px; display: inline-block; margin: 10px;">
Decline
</a>
</div>
</div>
</div>
{% endblock %}
3 changes: 2 additions & 1 deletion db/learning_circle.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ class UserCircleLink(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
circle = models.ForeignKey(LearningCircle, on_delete=models.CASCADE)
lead = models.BooleanField(default=False)
is_invited = models.BooleanField(default=False)
accepted = models.BooleanField()
accepted_at = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField()

class Meta:
managed = False
db_table = "user_circle_link"
db_table = "user_circle_link"
38 changes: 20 additions & 18 deletions utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
class CommonUtils:
@staticmethod
def get_paginated_queryset(
queryset: QuerySet, request, search_fields, sort_fields: dict = None
) -> QuerySet:
queryset: QuerySet, request, search_fields, sort_fields: dict = None
) -> QuerySet:
if sort_fields is None:
sort_fields = {}
sort_fields = { }

page = int(request.query_params.get("pageIndex", 1))
per_page = int(request.query_params.get("perPage", 10))
Expand All @@ -34,7 +34,7 @@ def get_paginated_queryset(
if search_query:
query = Q()
for field in search_fields:
query |= Q(**{f"{field}__icontains": search_query})
query |= Q(**{ f"{field}__icontains": search_query })

queryset = queryset.filter(query)

Expand Down Expand Up @@ -64,8 +64,8 @@ def get_paginated_queryset(
"nextPage": queryset.next_page_number()
if queryset.has_next()
else None,
},
}
},
}

@staticmethod
def generate_csv(queryset: QuerySet, csv_name: str) -> HttpResponse:
Expand All @@ -79,7 +79,7 @@ def generate_csv(queryset: QuerySet, csv_name: str) -> HttpResponse:
compressed_response = HttpResponse(
gzip.compress(response.content),
content_type="text/csv",
)
)
compressed_response[
"Content-Disposition"
] = f'attachment; filename="{csv_name}.csv"'
Expand Down Expand Up @@ -118,14 +118,13 @@ def format_time(date_time: datetime.datetime) -> datetime.datetime:

return date_time.replace(microsecond=0)


@staticmethod
def get_start_and_end_of_previous_month():
today = DateTimeUtils.get_current_utc_time()
start_date = today.replace(day=1)
end_date = start_date.replace(
day=1, month=start_date.month % 12 + 1
) - timedelta(days=1)
) - timedelta(days=1)
return start_date, end_date


Expand Down Expand Up @@ -154,7 +153,7 @@ def general_updates(category, action, *values) -> str:
for value in values:
content = f"{content}<|=|>{value}"
url = config("DISCORD_WEBHOOK_LINK")
data = {"content": content}
data = { "content": content }
requests.post(url, json=data)


Expand All @@ -167,15 +166,15 @@ def read_excel_file(self, file_obj):
for row in sheet.iter_rows(values_only=True):
row_dict = {
header.value: cell_value for header, cell_value in zip(sheet[1], row)
}
}
rows.append(row_dict)
workbook.close()

return rows


def send_template_mail(
context: dict, subject: str, address: list[str], attachment: str = None):
context: dict, subject: str, address: list[str], attachment: str = None):
"""
The function `send_user_mail` sends an email to a user with the provided user data, subject, and
address.
Expand All @@ -192,30 +191,33 @@ def send_template_mail(
from_mail = decouple.config("FROM_MAIL")

base_url = decouple.config("FR_DOMAIN_NAME")
status = None

email_content = render_to_string(
f"mails/{'/'.join(map(str, address))}", {"user": context, "base_url": base_url}
)
f"mails/{'/'.join(map(str, address))}", { "user": context, "base_url": base_url }
)
if not (mail := getattr(context, "email", None)):
mail = context["email"]

if attachment is None:
send_mail(
status = send_mail(
subject=subject,
message=email_content,
from_email=from_mail,
recipient_list=[mail],
html_message=email_content,
fail_silently=False,
)
)

else:
email = EmailMessage(
subject=subject,
body=email_content,
from_email=from_mail,
to=[context["email"]],
)
)
email.attach(attachment)
email.content_subtype = "html"
email.send()
status = email.send()

return status

0 comments on commit 3efc1ce

Please sign in to comment.