Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions api/common/common_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,8 @@ class ListIGAPI(APIView):
})},
)
def get(self, request):
return CustomResponse(response=InterestGroup.objects.all().values("name")).get_success_response()
from utils.types import InterestGroupStatus
return CustomResponse(response=InterestGroup.objects.filter(status=InterestGroupStatus.ACTIVE.value).values("name")).get_success_response()


class IGDetailAPI(APIView):
Expand All @@ -745,10 +746,11 @@ class IGDetailAPI(APIView):
)
def get(self, request, pk):
from api.dashboard.ig.dash_ig_serializer import InterestGroupSerializer
from utils.types import InterestGroupStatus

ig_data = (
InterestGroup.objects.prefetch_related("user_ig_link_ig")
.filter(id=pk)
.filter(id=pk, status=InterestGroupStatus.ACTIVE.value)
.first()
)

Expand Down
3 changes: 2 additions & 1 deletion api/dashboard/ig/dash_ig_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -878,8 +878,9 @@ class InterestGroupListApi(APIView):
responses={200: InterestGroupSerializer},
)
def get(self, request):
from utils.types import InterestGroupStatus
ig = (
InterestGroup.objects.all()
InterestGroup.objects.filter(status=InterestGroupStatus.ACTIVE.value)
.select_related("created_by", "updated_by")
.prefetch_related("user_ig_link_ig")
.annotate(members=Count("user_ig_link_ig"))
Expand Down
11 changes: 10 additions & 1 deletion api/dashboard/learningcircle/learningcircle_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1440,10 +1440,19 @@ def get(self, request):
@extend_schema(tags=['Dashboard - Learningcircle'], description="Create Circle Invite Status.",
responses={200: CircleInviteStatusSerializer},
)
def post(self, request, link_id: str):
def post(self, request, link_id: str = None):
"""Accept or reject an invitation by its link_id."""
user_id = JWTUtils.fetch_user_id(request)

if link_id is None:
link_id = request.data.get('link_id')

if not link_id:
return CustomResponse(
general_message="link_id is required"
).get_failure_response()


try:
link = UserCircleLink.objects.get(id=link_id, user_id=user_id, is_invited=True)
except UserCircleLink.DoesNotExist:
Expand Down
1 change: 1 addition & 0 deletions api/dashboard/learningcircle/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# init
160 changes: 160 additions & 0 deletions api/dashboard/learningcircle/tests/test_learningcircle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import pytest
from datetime import timedelta
import jwt
from django.conf import settings
from rest_framework.test import APIClient
from db.user import User
from db.task import InterestGroup
from db.learning_circle import LearningCircle, UserCircleLink
from utils.utils import DateTimeUtils

def get_auth_token(user_id):
expiry_dt = DateTimeUtils.get_current_utc_time() + timedelta(days=1)
expiry_str = expiry_dt.strftime("%Y-%m-%d %H:%M:%S%z")
payload = {
"id": user_id,
"expiry": expiry_str,
"roles": ["Student"]
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
return f"Bearer {token}"

@pytest.fixture
def user_fixture(db):
return User.objects.create(
id="user-1",
muid="USER1@mulearn",
full_name="Invited User",
email="invited@test.com",
)

@pytest.fixture
def inviter_fixture(db):
return User.objects.create(
id="user-2",
muid="USER2@mulearn",
full_name="Inviter User",
email="inviter@test.com",
)

@pytest.fixture
def ig_fixture(db, inviter_fixture):
return InterestGroup.objects.create(
id="ig-1",
name="Python",
code="PY",
icon="py",
created_by=inviter_fixture,
updated_by=inviter_fixture,
)

@pytest.fixture
def circle_fixture(db, ig_fixture, inviter_fixture):
return LearningCircle.objects.create(
id="circle-1",
ig=ig_fixture,
title="Python Circle",
created_by=inviter_fixture,
)

@pytest.fixture
def invite_link_fixture(db, user_fixture, circle_fixture, inviter_fixture):
return UserCircleLink.objects.create(
id="link-1234",
user=user_fixture,
circle=circle_fixture,
lead=False,
is_invited=True,
invited_by=inviter_fixture,
accepted=None,
)

@pytest.fixture
def auth_client(user_fixture):
client = APIClient()
token = get_auth_token(user_fixture.id)
client.credentials(HTTP_AUTHORIZATION=token)
return client

@pytest.mark.django_db
def test_accept_invite_via_body(auth_client, invite_link_fixture):
url = "/api/v1/dashboard/learningcircle/invite/status/"
payload = {
"link_id": invite_link_fixture.id,
"action": "accept"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 200
assert response.json()["message"]["general"][0] == "Invitation accepted successfully"

# Verify DB update
invite_link_fixture.refresh_from_db()
assert invite_link_fixture.accepted is True
assert invite_link_fixture.accepted_at is not None

@pytest.mark.django_db
def test_reject_invite_via_body(auth_client, invite_link_fixture):
url = "/api/v1/dashboard/learningcircle/invite/status/"
payload = {
"link_id": invite_link_fixture.id,
"action": "reject"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 200
assert response.json()["message"]["general"][0] == "Invitation rejected successfully"

# Verify DB update
invite_link_fixture.refresh_from_db()
assert invite_link_fixture.accepted is False
assert invite_link_fixture.accepted_at is None

@pytest.mark.django_db
def test_accept_invite_via_url(auth_client, invite_link_fixture):
url = f"/api/v1/dashboard/learningcircle/invite/status/{invite_link_fixture.id}/"
payload = {
"action": "accept"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 200
assert response.json()["message"]["general"][0] == "Invitation accepted successfully"

# Verify DB update
invite_link_fixture.refresh_from_db()
assert invite_link_fixture.accepted is True
assert invite_link_fixture.accepted_at is not None

@pytest.mark.django_db
def test_reject_invite_via_url(auth_client, invite_link_fixture):
url = f"/api/v1/dashboard/learningcircle/invite/status/{invite_link_fixture.id}/"
payload = {
"action": "reject"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 200
assert response.json()["message"]["general"][0] == "Invitation rejected successfully"

# Verify DB update
invite_link_fixture.refresh_from_db()
assert invite_link_fixture.accepted is False
assert invite_link_fixture.accepted_at is None

@pytest.mark.django_db
def test_invite_status_missing_link_id(auth_client):
url = "/api/v1/dashboard/learningcircle/invite/status/"
payload = {
"action": "accept"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 400
assert response.json()["message"]["general"][0] == "link_id is required"

@pytest.mark.django_db
def test_invite_status_invalid_link_id(auth_client):
url = "/api/v1/dashboard/learningcircle/invite/status/"
payload = {
"link_id": "non-existent-link-id",
"action": "accept"
}
response = auth_client.post(url, payload, format="json")
assert response.status_code == 400
assert response.json()["message"]["general"][0] == "Invitation not found"
3 changes: 2 additions & 1 deletion api/dashboard/projects/projects_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class ProjectSerializer(serializers.ModelSerializer):
updated_by = serializers.CharField(source="updated_by.full_name", read_only=True)
created_by = serializers.CharField(source="created_by.full_name", read_only=True)
created_by_id = serializers.CharField(source="created_by.id", read_only=True)
created_by_muid = serializers.CharField(source="created_by.muid", read_only=True)
logo = serializers.ImageField(max_length=None, use_url=True, required=False, allow_null=True)
images = ProjectImageSerializer(many=True, read_only=True)
links = ProjectLinkSerializer(many=True, read_only=True)
Expand All @@ -136,7 +137,7 @@ class Meta:
"id", "title", "description", "status",
"logo", "images", "links", "skills",
"members", "votes", "comments",
"created_by", "created_by_id", "updated_by",
"created_by", "created_by_id", "created_by_muid", "updated_by",
"created_at", "updated_at",
]

Expand Down
7 changes: 7 additions & 0 deletions utils/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ class IntegrationType(Enum):
KKEM = "DWMS"


class InterestGroupStatus(Enum):
ACTIVE = "active"
REQUESTED = "requested"
CANCELLED = "cancelled"
REJECTED = "rejected"


class TasksTypesHashtag(Enum):
REFERRAL = "referral"
MUCOIN = "mucoin"
Expand Down
Loading