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
11 changes: 11 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]

[requires]
python_version = "3.11"
100 changes: 47 additions & 53 deletions backend/penndata/management/commands/get_fitness_snapshot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import zoneinfo

import requests
from bs4 import BeautifulSoup
from dateutil import parser
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.db.models import Q
from django.utils.dateparse import parse_datetime
from django.utils.timezone import make_aware

from penndata.models import FitnessRoom, FitnessSnapshot

Expand All @@ -12,77 +15,68 @@ def cap_string(s):


def get_usages():

# count/capacities default to 0 since spreadsheet number appears blank if no one there
locations = [
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do we want to keep this as a sanity check? I'm assuming that because Penn rec is using a formal API now, we will always include all locations, but I defer.

"4th Floor Fitness",
"3rd Floor Fitness",
"2nd Floor Strength",
"Basketball Courts",
"MPR",
"Climbing Wall",
"1st Floor Fitness",
"Pool-Shallow",
"Pool-Deep",
]
usages = {location: {"count": 0, "capacity": 0} for location in locations}

date = timezone.localtime() # default if can't get date from spreadsheet

try:
resp = requests.get(
(
"https://docs.google.com/spreadsheets/u/0/d/e/"
"2PACX-1vSX91_MlAjJo5uVLznuy7BFnUgiBOI28oBCReLRKKo76L"
"-k8EFgizAYXpIKPBX_c76wC3aztn3BogD4"
"/pubhtml/sheet?headers=false&gid=0"
)
"https://goboardapi.azurewebsites.net/api/FacilityCount/GetCountsByAccount",
params={"AccountAPIKey": settings.FITNESS_TOKEN},
)
data = resp.json()
except ConnectionError:
return None

html = resp.content.decode("utf8")
soup = BeautifulSoup(html, "html5lib")
if not (embedded_spreadsheet := soup.find("tbody")):
except requests.exceptions.JSONDecodeError:
return None

table_rows = embedded_spreadsheet.findChildren("tr")
for i, row in enumerate(table_rows):
cells = row.findChildren("td")
if i == 0:
date = timezone.make_aware(parser.parse(cells[1].getText()))
elif (location := cap_string(cells[0].getText())) in usages:
try:
count = int(cells[1].getText())
capacity = float(cells[2].getText().strip("%"))
usages[location] = {"count": count, "capacity": capacity}
except ValueError:
pass
else:
print(f"Unknown location: {location}")
return usages, date
def location_aware_datetime(time_str):
date = parse_datetime(time_str)
timezone = zoneinfo.ZoneInfo("America/New_York")
return make_aware(date, timezone=timezone)

usages = {
location["LocationName"]: {
"count": location["LastCount"],
"capacity": location["TotalCapacity"],
"last_updated": location_aware_datetime(location["LastUpdatedDateAndTime"]),
}
for location in data
}
return usages


class Command(BaseCommand):
help = "Captures a new Fitness Snapshot for every Laundry room."
help = "Captures a new Fitness Snapshot for every Fitness room."

def handle(self, *args, **kwargs):
usage_by_location, date = get_usages()
# Don't update locations for which we already have a room with a matching last_updated date.
# Fixed the O(n^2) issue by loading everything into memory. Should be fine since there's
# not many rooms, and 1 snapshot returned per room
all_rooms = FitnessRoom.objects.all()
all_room_names = set(room.name for room in all_rooms)
query = Q()
for room_name, room_usage in get_usages().items():
query |= Q(room__name=room_name, date=room_usage["last_updated"])
existing_snapshots = FitnessSnapshot.objects.filter(query)
existing_room_date_pairs = set(
(snapshot.room.name, snapshot.date) for snapshot in existing_snapshots
)

# prevent double creating FitnessSnapshots
if FitnessSnapshot.objects.filter(date=date).exists():
self.stdout.write("FitnessSnapshots already exist for this date!")
return
def exists(record):
(name, usage) = record
if name not in all_room_names:
return False
if (name, usage["last_updated"]) in existing_room_date_pairs:
return False
return True

usage_by_location = filter(exists, get_usages().items())
FitnessSnapshot.objects.bulk_create(
[
FitnessSnapshot(
room=FitnessRoom.objects.get_or_create(name=room_name)[0],
date=date,
date=room_usage["last_updated"],
count=room_usage["count"],
capacity=room_usage["capacity"],
)
for room_name, room_usage in usage_by_location.items()
for (room_name, room_usage) in usage_by_location
]
)

Expand Down
23 changes: 11 additions & 12 deletions backend/penndata/management/commands/load_fitness_rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,23 @@
class Command(BaseCommand):
def handle(self, *args, **kwargs):
fitness_rooms = [
"4th Floor Fitness",
"3rd Floor Fitness",
"2nd Floor Strength",
"Basketball Courts",
"MPR",
"Climbing Wall",
"Rec Lounge",
"1st Floor Fitness",
"Pool-Shallow",
"Pool-Deep",
"Court 1",
"Court 2",
"Court 3",
"Multipurpose Room",
"2nd Floor Weight Room",
"3rd Floor Fitness Room",
"4th Floor Fitness Room",
"Studio 409",
"Sheerr Pool",
]
for room in fitness_rooms:
obj, _ = FitnessRoom.objects.get_or_create(name=room)
if obj.image_url == "":
s3_image_name = (
room.replace(" ", "_") + (".png" if "2nd" in room else ".jpg")
if "Pool" not in room
else "Pool.jpeg"
)
s3_image_name = room.replace(" ", "_") + ".jpg"
obj.image_url = (
f"https://s3.us-east-2.amazonaws.com/penn.mobile/pottruck/{s3_image_name}"
)
Expand Down
3 changes: 3 additions & 0 deletions backend/pennmobile/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
LIBCAL_SECRET = os.environ.get("LIBCAL_SECRET", None)
WHARTON_TOKEN = os.environ.get("WHARTON_TOKEN", None)

# Fitness Token
FITNESS_TOKEN = os.environ.get("FITNESS_TOKEN", None)

# Upload file storage
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID", None)
Expand Down
106 changes: 0 additions & 106 deletions backend/tests/penndata/fitness_snapshot.html

This file was deleted.

Loading
Loading