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

[Fix] Laundry Rewrite to Account For New SpeedQueen API #308

Merged
merged 13 commits into from
Nov 17, 2024
129 changes: 58 additions & 71 deletions backend/laundry/api_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,58 @@
import requests
from bs4 import BeautifulSoup
from django.conf import settings
from django.utils import timezone
from requests.exceptions import ConnectTimeout, HTTPError, ReadTimeout

from laundry.models import LaundryRoom, LaundrySnapshot


HALL_URL = f"{settings.LAUNDRY_URL}/?location="
def get_room_url(room_id: int):
return f"{settings.LAUNDRY_URL}/rooms/{room_id}/machines?raw=true"


def update_machine_object(cols, machine_object):
headers = {
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
"x-api-key": settings.LAUNDRY_X_API_KEY,
"alliancels-auth-token": settings.LAUNDRY_ALLIANCELS_API_KEY,
}


def update_machine_object(machine, machine_type_data):
"""
Updates Machine status and time remaining
"""

if cols[2].getText() in ["In use", "Almost done"]:
time_remaining = cols[3].getText().split(" ")[0]
machine_object["running"] += 1
# TODO: Early stage in update 9/29/2024, known status codes are "IN_USE", "AVAILABLE", "COMPLETE"; need to update
# TODO: if we identify other codes, especially error
status = machine["currentStatus"]["statusId"]
if status == "IN_USE":
time_remaining = machine[3].getText().split(" ")[0]
machine_type_data["running"] += 1
try:
machine_object["time_remaining"].append(int(time_remaining))
machine_type_data["time_remaining"].append(int(time_remaining))
except ValueError:
pass
elif cols[2].getText() == "Out of order":
machine_object["out_of_order"] += 1
elif cols[2].getText() == "Not online":
machine_object["offline"] += 1
elif status in ["AVAILABLE", "COMPLETE"]:
machine_type_data["open"] += 1
# elif status == "Out of order":
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
# machine_type_data["out_of_order"] += 1
# elif status == "Not online":
# machine_type_data["offline"] += 1
else:
machine_object["open"] += 1
machine_type_data["offline"] += 1

# edge case that handles machine not sending time data
diff = int(machine_object["running"]) - len(machine_object["time_remaining"])
# TODO: I don't think we need this?
diff = int(machine_type_data["running"]) - len(machine_type_data["time_remaining"])
while diff > 0:
machine_object["time_remaining"].append(-1)
machine_type_data["time_remaining"].append(-1)
diff = diff - 1

return machine_object
return machine_type_data


def parse_a_hall(hall_link):
def parse_a_room(room_request_link):
"""
Return names, hall numbers, and the washers/dryers available for a certain hall_id
Return names, hall numbers, and the washers/dryers available for a certain room_id
"""

washers = {"open": 0, "running": 0, "out_of_order": 0, "offline": 0, "time_remaining": []}
Expand All @@ -49,41 +61,28 @@ def parse_a_hall(hall_link):
detailed = []

try:
page = requests.get(
hall_link,
timeout=60,
headers={"Authorization": "Basic Sure-Nothing-Could-Go-Wrong-With-This-HaHa-Not"},
)
# page = requests.get(hall_link, timeout=60)
except (ConnectTimeout, ReadTimeout):
request = requests.get(room_request_link, timeout=60, headers=headers)
request.raise_for_status()
except HTTPError:
return {"washers": washers, "dryers": dryers, "details": detailed}

soup = BeautifulSoup(page.content, "html.parser")
soup.prettify()

rows = soup.find_all("tr")
for row in rows:
cols = row.find_all("td")
if len(cols) > 1:
machine_type = cols[1].getText()
if machine_type == "Washer":
washers = update_machine_object(cols, washers)
elif machine_type == "Dryer":
dryers = update_machine_object(cols, dryers)
if machine_type in ["Washer", "Dryer"]:
try:
time = int(cols[3].getText().split(" ")[0])
except ValueError:
time = 0
detailed.append(
{
"id": int(cols[0].getText().split(" ")[1][1:]),
"type": cols[1].getText().lower(),
"status": cols[2].getText(),
"time_remaining": time,
}
)

request_json = request.json()

for machine in request_json:
if machine["isWasher"]:
washers = update_machine_object(machine, washers)
elif machine["isDryer"]:
dryers = update_machine_object(machine, dryers)
if machine["isWasher"] or machine["isDryer"]:
# TODO: Unsure if remaining seconds and the new status ids work on frontend
detailed.append(
{
"id": machine["id"],
"type": "washer" if machine["isWasher"] else "dryer",
"status": machine["currentStatus"]["statusId"],
"time_remaining": machine["currentStatus"]["remainingSeconds"],
}
)
return {"washers": washers, "dryers": dryers, "details": detailed}
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved


Expand All @@ -93,25 +92,14 @@ def check_is_working():
"""

try:
r = requests.post(
"{}/".format(settings.LAUNDRY_URL),
timeout=60,
headers={"Authorization": "Basic Sure-Nothing-Could-Go-Wrong-With-This-HaHa-Not"},
data={
"locationid": "5faec7e9-a4aa-47c2-a514-950c03fac460",
"email": "[email protected]",
"washers": 0,
"dryers": 0,
"locationalert": "OK",
},
)
r.raise_for_status()
return (
"The transaction log for database 'QuantumCoin' is full due to 'LOG_BACKUP'."
not in r.text
all_rooms_request = requests.get(
f"{settings.LAUNDRY_URL}/geoBoundaries/5610?raw=true", timeout=60, headers=headers
)
except HTTPError:
all_rooms_request.raise_for_status()

except HTTPError as e:
return False
return True


def all_status():
Expand All @@ -120,16 +108,16 @@ def all_status():
"""

return {
room.name: parse_a_hall(HALL_URL + str(room.uuid)) for room in LaundryRoom.objects.all()
room.name: parse_a_room(get_room_url(room.room_id)) for room in LaundryRoom.objects.all()
}


def hall_status(room):
def room_status(room):
"""
Return the status of each specific washer/dryer in a particular hall_id
"""

machines = parse_a_hall(HALL_URL + str(room.uuid))
machines = parse_a_room(get_room_url(room.room_id))

return {"machines": machines, "hall_name": room.name, "location": room.location}

Expand All @@ -145,7 +133,6 @@ def save_data():
data = all_status()

for name, room in data.items():

laundry_room = LaundryRoom.objects.get(name=name)

LaundrySnapshot.objects.create(
Expand Down
53 changes: 53 additions & 0 deletions backend/laundry/data/laundry_data_new.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
14089,English House,English House,14146,3,3
14082,"Gregory College House: Class of 1925""",Gregory College House: Class of 1925,14138,4,4
14085,Gregory College House: Van Pelt,Gregory College House: Van Pelt,14141,6,6
5249,Gutmann College House,Gutmann College House,5611,18,18
14099,Harnwell 10th Floor,Harnwell College House,14150,3,3
14100,Harnwell 12th Floor,Harnwell College House,14150,3,3
14101,Harnwell 14th Floor,Harnwell College House,14150,3,3
14102,Harnwell 16th Floor,Harnwell College House,14150,3,3
14103,Harnwell 18th Floor,Harnwell College House,14150,3,3
14104,Harnwell 20th Floor,Harnwell College House,14150,3,3
14105,Harnwell 22nd Floor,Harnwell College House,14150,3,3
14106,Harnwell 24th Floor,Harnwell College House,14150,3,3
14094,Harnwell 2nd Floor,Harnwell College House,14150,3,3
14095,Harnwell 4th Floor,Harnwell College House,14150,3,3
14096,Harnwell 6th Floor,Harnwell College House,14150,3,3
14098,Harnwell 8th Floor,Harnwell College House,14150,3,3
14111,Harrison 10th Floor,Harrison College House,14153,3,3
14112,Harrison 12th Floor ,Harrison College House,14153,3,3
14113,Harrison 14th Floor,Harrison College House,14153,3,3
14114,Harrison 16th Floor,Harrison College House,14153,3,3
14115,Harrison 18th Floor ,Harrison College House,14153,3,3
14116,Harrison 20th Floor,Harrison College House,14153,3,3
14117,Harrison 22nd Floor,Harrison College House,14153,3,3
14118,Harrison 24th Floor,Harrison College House,14153,3,3
14107,Harrison 2nd Floor,Harrison College House,14153,3,3
14108,Harrison 4th Floor,Harrison College House,14153,3,3
14109,Harrison 6th Floor,Harrison College House,14153,3,3
14110,Harrison 8th Floor,Harrison College House,14153,3,3
14090,Hill College House,Hill College House,14147,16,16
14091,King's Court,King's Court,14148,5,6
14092,Lauder,Lauder,14149,10,10
14078,Class of 28,Quad: Fisher College House,14655,0,0
14079,Craig,Quad: Fisher College House,14655,0,0
14080,Ashhurst - Magee,Quad: Riepe College House,14137,12,12
14076,Birthday - Bishop White,Quad: Riepe College House,14137,9,9
14077,Butcher - Chestnut,Quad: Ware College House,14656,0,0
14081,Morgan,Quad: Ware College House,14656,0,0
14123,Rodin 10th Floor,Rodin College House,14154,3,3
14124,Rodin 12th Floor,Rodin College House,14154,3,3
14125,Rodin 14th Floor,Rodin College House,14154,3,3
14126,Rodin 16th Floor,Rodin College House,14154,3,3
14127,Rodin 18th Floor,Rodin College House,14154,3,3
14128,Rodin 20th Floor,Rodin College House,14154,3,3
14129,Rodin 22nd Floor,Rodin College House,14154,3,3
14130,Rodin 24th Floor,Rodin College House,14154,3,3
14119,Rodin 2nd Floor,Rodin College House,14154,3,3
14120,Rodin 4th Floor,Rodin College House,14154,3,3
14121,Rodin 6th Floor,Rodin College House,14154,3,3
14122,Rodin 8th Floor,Rodin College House,14154,3,3
14084,Samson West,Samson West,14140,16,16
11373,Stouffer Commons,Stouffer College House,11693,4,4
14093,Stouffer Mayer,Stouffer College House,11693,8,8
14083,W.E.B. Du Bois College House,W.E.B. Du Bois College House,14139,6,6
13 changes: 7 additions & 6 deletions backend/laundry/management/commands/load_laundry_rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
class Command(BaseCommand):
def handle(self, *args, **kwargs):

with open("laundry/data/laundry_data.csv") as data:
with open("laundry/data/laundry_data_new.csv") as data:
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
reader = csv.reader(data)

for i, row in enumerate(reader):
hall_id, hall_name, location, uuid, total_washers, total_dryers = row
room_id, name, location, location_id, total_washers, total_dryers = row

LaundryRoom.objects.get_or_create(
hall_id=int(hall_id),
name=hall_name,
LaundryRoom.objects.create(
room_id=room_id,
name=name,
location=location,
uuid=uuid,
location_id=location_id,
total_washers=total_washers,
total_dryers=total_dryers,
new=True,
)

self.stdout.write("Uploaded Laundry Rooms!")
66 changes: 66 additions & 0 deletions backend/laundry/management/commands/update_laundry_rooms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import csv
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
from functools import reduce

import requests
from django.conf import settings
from django.core.management.base import BaseCommand
from requests.exceptions import HTTPError


class Command(BaseCommand):
help = "Update laundry rooms csv from server"

def handle(self, *args, **kwargs):
# Pull initial request with everything
try:
headers = {
"x-api-key": settings.LAUNDRY_X_API_KEY,
"alliancels-auth-token": settings.LAUNDRY_ALLIANCELS_API_KEY,
}
all_rooms_request = requests.get(
f"{settings.LAUNDRY_URL}/geoBoundaries/5610?raw=true", timeout=60, headers=headers
)
all_rooms_request.raise_for_status()

except HTTPError as e:
self.stdout.write(f"Error: {e}")
return

all_rooms_request_json = all_rooms_request.json()
locations = all_rooms_request_json["geoBoundaries"][0]["geoBoundaries"]

laundry_rooms = [
[room["id"], room["roomName"], location["description"], location["id"]]
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
for location in locations
for room in location["rooms"]
]

# for each room, send a request to find number of washers and dryers
# TODO: This is really inefficient, but may require change in frontend code so leaving it for now
for room in laundry_rooms:
try:
room_request = requests.get(
f"{settings.LAUNDRY_URL}/rooms/{room[0]}/machines?raw=true",
timeout=60,
headers=headers,
)
room_request.raise_for_status()
except HTTPError as e:
self.stdout.write(f"Error: {e}")
return

room_request_json = room_request.json()
# count washers and dryers
count = (0, 0)
f = lambda acc, x: (
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
(acc[0] + 1, acc[1])
if x["isWasher"]
else (acc[0], acc[1] + 1) if x["isDryer"] else acc
)
count = reduce(f, room_request_json, count)
room.extend(count)

# write to csv
with open("laundry/data/laundry_data_new.csv", "w") as data:
dr-Jess marked this conversation as resolved.
Show resolved Hide resolved
writer = csv.writer(data)
writer.writerows(laundry_rooms)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.0.2 on 2024-09-29 06:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("laundry", "0002_auto_20210321_1105"),
]

operations = [
migrations.RenameField(
model_name="laundryroom",
old_name="hall_id",
new_name="room_id",
),
migrations.RemoveField(
model_name="laundryroom",
name="uuid",
),
migrations.AddField(
model_name="laundryroom",
name="location_id",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="laundryroom",
name="new",
field=models.BooleanField(default=False),
),
]
Loading
Loading