Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IEEE-21 /api/event/teams/team #280

Merged
merged 23 commits into from
Jul 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0512791
Updated with full serializer, views and test cases
arshinmar Jun 5, 2021
44f41c8
Added some updated tests
arshinmar Jun 5, 2021
c0433cd
Expected final tests
arshinmar Jun 5, 2021
f1a29f6
Updated with truly final tests, pending comment
arshinmar Jun 7, 2021
5223eda
Merge branch 'develop' into 21-api-team
arshinmar Jun 8, 2021
7b9d8b6
Updated with proper message formatting
arshinmar Jun 11, 2021
aa64cf3
Merge branch '21-api-team' of github.com:ieeeuoft/hackathon-template …
arshinmar Jun 11, 2021
bcee2bb
Added stuff to gitignore, and formatting changed
arshinmar Jun 11, 2021
3220405
Update .gitignore
arshinmar Jun 12, 2021
2220f1d
Update views.py
arshinmar Jun 12, 2021
ec739ed
Update views.py
arshinmar Jun 12, 2021
7ff4af3
Update views.py
arshinmar Jun 12, 2021
cea4904
Update tests.py
arshinmar Jun 12, 2021
2c85169
Update test_api.py
arshinmar Jun 12, 2021
b3f8362
Changed profiles related name to profile
arshinmar Jun 19, 2021
6e3f349
Merge branch '21-api-team' of github.com:ieeeuoft/hackathon-template …
arshinmar Jun 19, 2021
16f8673
Update serializers.py
arshinmar Jun 22, 2021
3fbf88e
Merge branch 'develop' into 21-api-team
arshinmar Jun 22, 2021
0fa45fb
Updated with profile views
arshinmar Jun 22, 2021
6afc15d
Merge branch '21-api-team' of github.com:ieeeuoft/hackathon-template …
arshinmar Jun 22, 2021
fb24b71
All tests work
arshinmar Jun 22, 2021
ef078d1
One test still failing despite changed imports
arshinmar Jun 26, 2021
b9d6c71
Removed unused imports
arshinmar Jul 3, 2021
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ lib/
lib64/
parts/
sdist/
ieee-virtual/
var/
wheels/
share/python-wheels/
Expand Down Expand Up @@ -154,3 +155,6 @@ hackathon_site/**/*.css.map

# node_modules, found outside the react app
node_modules/

# OS Stuff
.DS_Store
3 changes: 2 additions & 1 deletion hackathon_site/event/api_urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.urls import path

from event import api_views
from event import api_views, views

app_name = "event"

urlpatterns = [
path("users/user/", api_views.CurrentUserAPIView.as_view(), name="current-user"),
path("teams/team/", views.CurrentTeamAPIView.as_view(), name="current-team"),
]
14 changes: 13 additions & 1 deletion hackathon_site/event/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.contrib.auth.models import Group
from rest_framework import serializers

from event.models import Profile, User
from event.models import Profile, User, Team


class GroupSerializer(serializers.ModelSerializer):
Expand All @@ -10,6 +10,17 @@ class Meta:
fields = ("id", "name")


class TeamSerializer(serializers.ModelSerializer):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't in the ticket, but I think we should add a profiles serializer into here. That way, on the frontend, we can just get /api/event/teams/team/, and we'll have all the information we need about the team (aka, the members on it), rather than having to make a separate request.

When we eventually have a list view of /api/event/teams/ for the admin side of the site, that will also come in handy so we don't have to fetch the list of team members separately.

To do that, you won't want to use the ProfileSerializer below, since that includes the team - so you'll end up with a circular reference. I would create a new ProfileInTeamSerializer, that includes the profile as well as important related user fields - first name, last name, email. I think you can just put user__first_name etc in the fields list, otherwise you'll need a user serializer too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, disregard this for now. I'll create a separate ticket, so that we don't get in the habit of scope creep (which I am bad for).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, is it ok if I resolve this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep it unresolved just so it's visible for future reference

class Meta:
model = Team
fields = (
"id",
"team_code",
"created_at",
"updated_at",
)


class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
Expand All @@ -19,6 +30,7 @@ class Meta:
"attended",
"acknowledge_rules",
"e_signature",
"team",
)


Expand Down
68 changes: 48 additions & 20 deletions hackathon_site/event/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from rest_framework.test import APITestCase
from hackathon_site.tests import SetupUserMixin

from event.models import Profile, User
from event.models import Profile, User, Team
from event.serializers import (
TeamSerializer,
UserSerializer,
)


class CurrentUserTestCase(SetupUserMixin, APITestCase):
Expand Down Expand Up @@ -37,25 +41,49 @@ def test_user_has_no_profile(self):
self.assertEqual(expected_response, data)

def test_user_has_profile(self):
expected_response = {
**{
attr: getattr(self.user, attr)
for attr in ("id", "first_name", "last_name", "email")
},
"profile": {
attr: getattr(self.profile, attr)
for attr in (
"id",
"id_provided",
"attended",
"acknowledge_rules",
"e_signature",
)
},
"groups": [{"id": self.group.id, "name": self.group.name}],
}
self._login()
response = self.client.get(self.view)

user_expected = User.objects.get(pk=self.user.pk)
serializer = UserSerializer(user_expected)

self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.json()
self.assertEqual(data, expected_response)
self.assertEqual(response.json(), serializer.data)


class CurrentTeamTestCase(SetupUserMixin, APITestCase):
def setUp(self):
super().setUp()
self.group = Group.objects.create(name="Test Users")
self.user.groups.add(self.group)
self.team = Team.objects.create()

self.profile = Profile.objects.create(user=self.user, team=self.team)

self.view = reverse("api:event:current-team")

def test_user_not_logged_in(self):
response = self.client.get(self.view)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_user_has_no_profile(self):
"""
When the user attempts to access the team, while it has no profile.
The user must be accepted or waitlisted to have formed a team.
"""
self.profile.delete()
self._login()
response = self.client.get(self.view)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_user_has_profile(self):
"""
When user has a profile and attempts to access the team, then the user
should get the correct response.
"""
self._login()
response = self.client.get(self.view)
team_expected = Team.objects.get(pk=self.team.pk)
serializer = TeamSerializer(team_expected)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json(), serializer.data)
81 changes: 75 additions & 6 deletions hackathon_site/event/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@
from unittest.mock import patch
from datetime import datetime, timedelta

from django.conf import settings
from django.core import mail
from django.contrib.auth.models import Group
from django.conf import settings
from django.test import TestCase
from django.urls import reverse
from rest_framework import status

from event.models import Profile, Team, User
from event.models import Profile, User, Team as EventTeam
from hackathon_site.tests import SetupUserMixin
from registration.models import Team as RegistrationTeam

from event.serializers import (
UserSerializer,
GroupSerializer,
ProfileSerializer,
)


class ProfileTestCase(TestCase):
def setUp(self):
Expand All @@ -23,15 +30,15 @@ def setUp(self):
)

def test_creates_profile_with_provided_team(self):
team = Team.objects.create()
team = EventTeam.objects.create()
profile = Profile.objects.create(user=self.user, team=team)
self.assertEqual(Team.objects.count(), 1)
self.assertEqual(EventTeam.objects.count(), 1)
self.assertEqual(profile.team, team)

def test_creates_team_if_not_provided(self):
profile = Profile.objects.create(user=self.user)
self.assertEqual(Team.objects.count(), 1)
self.assertEqual(Team.objects.first(), profile.team)
self.assertEqual(EventTeam.objects.count(), 1)
self.assertEqual(EventTeam.objects.first(), profile.team)


class IndexViewTestCase(SetupUserMixin, TestCase):
Expand Down Expand Up @@ -648,3 +655,65 @@ def test_reset_password_confirm_valid_submit_redirect(self):
self.assertContains(
redirected_response, "Your password has been successfully reset"
)


class UserSerializerTestCase(TestCase):
def test_serializer(self):
team = EventTeam.objects.create()
group = Group.objects.create(name="Test")
user = User.objects.create()
user.groups.add(group)

Profile.objects.create(
user=user, team=team,
)

user_serialized = UserSerializer(user).data
profile_serialized = ProfileSerializer(user.profile).data
group_serialized = GroupSerializer(user.groups, many=True).data

user_expected = {
"id": user.id,
"first_name": user.first_name,
"last_name": user.last_name,
"email": user.email,
"profile": profile_serialized,
"groups": group_serialized,
}
self.assertEqual(user_expected, user_serialized)


class GroupSerializerTestCase(TestCase):
def test_serializer(self):
group = Group.objects.create(name="Test")
group_serialized = GroupSerializer(group).data
group_expected = {
"id": group.id,
"name": group.name,
}
self.assertEqual(group_expected, group_serialized)


class ProfileSerializerTestCase(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username="foo@bar.com",
password="foobar123",
first_name="Foo",
last_name="Bar",
)

def test_serializer(self):
team = EventTeam.objects.create()

profile = Profile.objects.create(user=self.user, team=team)
profile_serialized = ProfileSerializer(profile).data
profile_expected = {
"id": profile.id,
"id_provided": profile.id_provided,
"attended": profile.attended,
"acknowledge_rules": profile.acknowledge_rules,
"e_signature": profile.e_signature,
"team": team.id,
}
self.assertEqual(profile_expected, profile_serialized)
34 changes: 32 additions & 2 deletions hackathon_site/event/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

from hackathon_site.utils import is_registration_open
from registration.forms import JoinTeamForm
from registration.models import Team
from registration.models import Team as RegistrationTeam

from rest_framework import generics, mixins

from event.models import Team as EventTeam
from event.serializers import TeamSerializer


def _now():
Expand All @@ -33,6 +38,29 @@ def get_context_data(self, **kwargs):
return context


class CurrentTeamAPIView(generics.GenericAPIView, mixins.RetrieveModelMixin):
"""
View to handle API interaction with the current logged in user's EventTeam
"""

queryset = EventTeam.objects.all()
serializer_class = TeamSerializer

def get_object(self):
queryset = self.get_queryset()

return generics.get_object_or_404(
queryset, profiles__user_id=self.request.user.id
)

def get(self, request, *args, **kwargs):
"""
Get the current users team profile and team details
Reads the profile of the current logged in team.
"""
return self.retrieve(request, *args, **kwargs)


class DashboardView(LoginRequiredMixin, FormView):
template_name = "event/dashboard_base.html"
# Form submits should take the user back to the dashboard
Expand Down Expand Up @@ -70,7 +98,9 @@ def form_valid(self, form):

if isinstance(form, JoinTeamForm):
application = self.request.user.application
new_team = Team.objects.get(team_code=form.cleaned_data["team_code"])
new_team = RegistrationTeam.objects.get(
team_code=form.cleaned_data["team_code"]
)
old_team = application.team

application.team = new_team
Expand Down