Skip to content

Commit

Permalink
Revert "GSR Revamp (#224)"
Browse files Browse the repository at this point in the history
This reverts commit f660ef4.
  • Loading branch information
vcai122 authored Dec 12, 2023
1 parent 776dab2 commit 67a605e
Show file tree
Hide file tree
Showing 9 changed files with 603 additions and 512 deletions.
763 changes: 392 additions & 371 deletions backend/gsr_booking/api_wrapper.py

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions backend/gsr_booking/group_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import datetime
import random

from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404

from gsr_booking.api_wrapper import APIError, BookingWrapper, CreditType
from gsr_booking.models import GSR, GroupMembership, Reservation


User = get_user_model()


class GroupBook:
def __init__(self):
self.bw = BookingWrapper()

def get_wharton_users(self, group):
"""
Returns list of wharton users of a Group in random ordering
"""
# TODO: filter for pennkey allowed
wharton_users = list(GroupMembership.objects.filter(group=group, is_wharton=True))
# shuffle to prevent sequential booking
random.shuffle(wharton_users)
return wharton_users

def get_all_users(self, group):
"""
Returns list of all users of a Group in random ordering
"""
# TODO: filter for pennkey allowed
all_users = list(GroupMembership.objects.filter(group=group))
# shuffle to prevent sequential booking
random.shuffle(all_users)
return all_users

def book_room(self, gid, rid, room_name, start, end, user, group):
"""
Book function for Group
"""
# TODO: check credits
gsr = get_object_or_404(GSR, gid=gid)

start = datetime.datetime.strptime(start, "%Y-%m-%dT%H:%M:%S%z")
end = datetime.datetime.strptime(end, "%Y-%m-%dT%H:%M:%S%z")

# sets users and credit_id depending on the gsr type
if gsr.kind == GSR.KIND_WHARTON:
users = self.get_wharton_users(group)
credit_id = gsr.lid
else:
users = self.get_all_users(group)
credit_id = CreditType.LIBCAL.value

total_credits = sum([self.bw.check_credits(usr.user).get(credit_id, 0) for usr in users])
duration = int((end.timestamp() - start.timestamp()) / 60)
if total_credits < duration:
raise APIError("Not Enough Credits to Book")

Check warning on line 59 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L59

Added line #L59 was not covered by tests
if duration % 30 != 0:
raise APIError("Invalid duration")

Check warning on line 61 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L61

Added line #L61 was not covered by tests

# creates reservation object to be used to group each booking
reservation = Reservation.objects.create(start=start, end=end, creator=user, group=group)

# we could potentially repeat using a user to make a 30 min booking so
# loop until total duration booked
while duration > 0:
# loop through each user and try to make a 30 min booking
# under that user if they have enough credits
for usr in users:
credit = self.bw.check_credits(usr.user).get(credit_id, 0)
if credit < 30:
continue

Check warning on line 74 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L74

Added line #L74 was not covered by tests
curr_end = start + datetime.timedelta(minutes=30) # end of current booking
booking = self.bw.book_room(
gid,
rid,
room_name,
start.strftime("%Y-%m-%dT%H:%M:%S%z"),
curr_end.strftime("%Y-%m-%dT%H:%M:%S%z"),
usr.user,
group_book=True,
)
booking.reservation = reservation
booking.save()
# update new start and duration appropriately
start = curr_end
duration -= 30
if duration <= 0:
break
return reservation

def get_availability(self, lid, gid, start, end, user, group):
"""
Availability function for Group
"""

gsr = GSR.objects.filter(gid=gid).first()
if gsr.kind == GSR.KIND_WHARTON:

Check warning on line 100 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L99-L100

Added lines #L99 - L100 were not covered by tests
# check if wharton users is non-empty
wharton_user = GroupMembership.objects.filter(group=group, is_wharton=True).first()
if wharton_user:
return self.bw.get_availability(lid, gid, start, end, wharton_user.user)

Check warning on line 104 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L102-L104

Added lines #L102 - L104 were not covered by tests

return self.bw.get_availability(lid, gid, start, end, user)

Check warning on line 106 in backend/gsr_booking/group_logic.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/group_logic.py#L106

Added line #L106 was not covered by tests
24 changes: 0 additions & 24 deletions backend/gsr_booking/migrations/0011_alter_reservation_group.py

This file was deleted.

24 changes: 18 additions & 6 deletions backend/gsr_booking/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from django.utils import timezone
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout


User = get_user_model()
Expand Down Expand Up @@ -42,7 +45,20 @@ def save(self, *args, **kwargs):
super().save(*args, **kwargs)

def check_wharton(self):
return WhartonGSRBooker.is_wharton(self.user)
# not using api_wrapper.py to prevent circular dependency
url = f"https://apps.wharton.upenn.edu/gsr/api/v1/{self.user.username}/privileges"
try:
response = requests.get(

Check warning on line 51 in backend/gsr_booking/models.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/models.py#L49-L51

Added lines #L49 - L51 were not covered by tests
url, headers={"Authorization": f"Token {settings.WHARTON_TOKEN}"}
)

if response.status_code != 200:
return None

Check warning on line 56 in backend/gsr_booking/models.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/models.py#L55-L56

Added lines #L55 - L56 were not covered by tests

res_json = response.json()
return res_json.get("type") == "whartonMBA" or res_json.get("type") == "whartonUGR"
except (ConnectTimeout, ReadTimeout, KeyError, ConnectionError):
return None

Check warning on line 61 in backend/gsr_booking/models.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/models.py#L58-L61

Added lines #L58 - L61 were not covered by tests

class Meta:
verbose_name = "Group Membership"
Expand Down Expand Up @@ -104,7 +120,7 @@ class Reservation(models.Model):
start = models.DateTimeField(default=timezone.now)
end = models.DateTimeField(default=timezone.now)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
is_cancelled = models.BooleanField(default=False)
reminder_sent = models.BooleanField(default=False)

Expand All @@ -120,7 +136,3 @@ class GSRBooking(models.Model):
start = models.DateTimeField(default=timezone.now)
end = models.DateTimeField(default=timezone.now)
is_cancelled = models.BooleanField(default=False)


# import at end to prevent circular dependency
from gsr_booking.api_wrapper import WhartonGSRBooker # noqa: E402
2 changes: 2 additions & 0 deletions backend/gsr_booking/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
BookRoom,
CancelRoom,
CheckWharton,
CreditsView,
GroupMembershipViewSet,
GroupViewSet,
Locations,
Expand All @@ -32,4 +33,5 @@
path("book/", BookRoom.as_view(), name="book"),
path("cancel/", CancelRoom.as_view(), name="cancel"),
path("reservations/", ReservationsView.as_view(), name="reservations"),
path("credits/", CreditsView.as_view(), name="credits"),
]
61 changes: 31 additions & 30 deletions backend/gsr_booking/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from gsr_booking.api_wrapper import APIError, GSRBooker, WhartonGSRBooker
from gsr_booking.api_wrapper import APIError, BookingWrapper
from gsr_booking.group_logic import GroupBook
from gsr_booking.models import GSR, Group, GroupMembership, GSRBooking
from gsr_booking.serializers import (
GroupMembershipSerializer,
Expand Down Expand Up @@ -163,6 +164,11 @@ def get_queryset(self):
)


# umbrella class used for accessing GSR API's (needed for token authentication)
BW = BookingWrapper()
GB = GroupBook()


class Locations(generics.ListAPIView):
"""Lists all available locations to book from"""

Expand All @@ -186,11 +192,8 @@ def get_queryset(self):


class CheckWharton(APIView):

permission_classes = [IsAuthenticated]

def get(self, request):
return Response({"is_wharton": WhartonGSRBooker.is_wharton(request.user)})
return Response({"is_wharton": BW.is_wharton(request.user)})


class Availability(APIView):
Expand All @@ -208,16 +211,11 @@ def get(self, request, lid, gid):
end = request.GET.get("end")

try:
return Response(
GSRBooker.get_availability(
lid,
gid,
start,
end,
request.user,
request.user.booking_groups.filter(name="Penn Labs").first(),
)
)
group = Group.objects.get(name="Penn Labs")
if request.user in group.members.all():
return Response(GB.get_availability(lid, gid, start, end, request.user, group))

Check warning on line 216 in backend/gsr_booking/views.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/views.py#L216

Added line #L216 was not covered by tests
else:
return Response(BW.get_availability(lid, gid, start, end, request.user))
except APIError as e:
return Response({"error": str(e)}, status=400)

Expand All @@ -235,15 +233,11 @@ def post(self, request):
room_name = request.data["room_name"]

try:
GSRBooker.book_room(
gid,
room_id,
room_name,
start,
end,
request.user,
request.user.booking_groups.filter(name="Penn Labs").first(),
)
group = Group.objects.get(name="Penn Labs")
if request.user in group.members.all():
GB.book_room(gid, room_id, room_name, start, end, request.user, group)

Check warning on line 238 in backend/gsr_booking/views.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/views.py#L238

Added line #L238 was not covered by tests
else:
BW.book_room(gid, room_id, room_name, start, end, request.user)
return Response({"detail": "success"})
except APIError as e:
return Response({"error": str(e)}, status=400)
Expand All @@ -260,7 +254,7 @@ def post(self, request):
booking_id = request.data["booking_id"]

try:
GSRBooker.cancel_room(booking_id, request.user)
BW.cancel_room(booking_id, request.user)
return Response({"detail": "success"})
except APIError as e:
return Response({"error": str(e)}, status=400)
Expand All @@ -274,8 +268,15 @@ class ReservationsView(APIView):
permission_classes = [IsAuthenticated]

def get(self, request):
return Response(
GSRBooker.get_reservations(
request.user, request.user.booking_groups.filter(name="Penn Labs").first()
)
)
return Response(BW.get_reservations(request.user))


class CreditsView(APIView):
"""
Gets credits for a User
"""

permission_classes = [IsAuthenticated]

def get(self, request):
return Response(BW.check_credits(request.user))

Check warning on line 282 in backend/gsr_booking/views.py

View check run for this annotation

Codecov / codecov/patch

backend/gsr_booking/views.py#L282

Added line #L282 was not covered by tests
1 change: 0 additions & 1 deletion backend/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
max-line-length = 100
exclude = .venv, migrations
inline-quotes = double
ignore = E203, W503

[isort]
default_section = THIRDPARTY
Expand Down
16 changes: 8 additions & 8 deletions backend/tests/gsr_booking/test_gsr_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,21 +92,21 @@ def test_recent(self):
self.assertIn("image_url", res_json[0])
self.assertNotEqual(res_json[0]["id"], res_json[1]["id"])

@mock.patch("gsr_booking.api_wrapper.WhartonBookingWrapper.is_wharton", is_wharton_false)
@mock.patch("gsr_booking.views.BW.is_wharton", is_wharton_false)
def test_get_wharton_false(self):
response = self.client.get(reverse("is-wharton"))
res_json = json.loads(response.content)
self.assertEqual(1, len(res_json))
self.assertFalse(res_json["is_wharton"])

@mock.patch("gsr_booking.api_wrapper.WhartonBookingWrapper.is_wharton", is_wharton_true)
@mock.patch("gsr_booking.views.BW.is_wharton", is_wharton_true)
def test_get_wharton_true(self):
response = self.client.get(reverse("is-wharton"))
res_json = json.loads(response.content)
self.assertEqual(1, len(res_json))
self.assertTrue(res_json["is_wharton"])

@mock.patch("gsr_booking.api_wrapper.BookingHandler.get_availability", libcal_availability)
@mock.patch("gsr_booking.views.BW.get_availability", libcal_availability)
def test_availability_libcal(self):
response = self.client.get(reverse("availability", args=["1086", "1889"]))
res_json = json.loads(response.content)
Expand All @@ -120,7 +120,7 @@ def test_availability_libcal(self):
self.assertIn("id", room)
self.assertIn("availability", room)

@mock.patch("gsr_booking.api_wrapper.BookingHandler.get_availability", wharton_availability)
@mock.patch("gsr_booking.views.BW.get_availability", wharton_availability)
def test_availability_wharton(self):
response = self.client.get(reverse("availability", args=["JMHH", "1"]))
res_json = json.loads(response.content)
Expand All @@ -134,7 +134,7 @@ def test_availability_wharton(self):
self.assertIn("id", room)
self.assertIn("availability", room)

@mock.patch("gsr_booking.api_wrapper.BookingHandler.book_room", book_cancel_room)
@mock.patch("gsr_booking.views.BW.book_room", book_cancel_room)
def test_book_libcal(self):
payload = {
"start_time": "2021-11-21T18:30:00-05:00",
Expand All @@ -150,7 +150,7 @@ def test_book_libcal(self):
self.assertEqual(1, len(res_json))
self.assertEqual("success", res_json["detail"])

@mock.patch("gsr_booking.api_wrapper.BookingHandler.book_room", book_cancel_room)
@mock.patch("gsr_booking.views.BW.book_room", book_cancel_room)
def test_book_wharton(self):
payload = {
"start_time": "2021-11-21T18:30:00-05:00",
Expand All @@ -166,7 +166,7 @@ def test_book_wharton(self):
self.assertEqual(1, len(res_json))
self.assertEqual("success", res_json["detail"])

@mock.patch("gsr_booking.api_wrapper.BookingHandler.cancel_room", book_cancel_room)
@mock.patch("gsr_booking.views.BW.cancel_room", book_cancel_room)
def test_cancel_room(self):
payload = {"booking_id": "booking id"}
response = self.client.post(
Expand All @@ -176,7 +176,7 @@ def test_cancel_room(self):
self.assertEqual(1, len(res_json))
self.assertEqual("success", res_json["detail"])

@mock.patch("gsr_booking.api_wrapper.BookingHandler.get_reservations", reservations)
@mock.patch("gsr_booking.views.BW.get_reservations", reservations)
def test_reservations(self):
response = self.client.get(reverse("reservations"))
res_json = json.loads(response.content)
Expand Down
Loading

0 comments on commit 67a605e

Please sign in to comment.