diff --git a/backend/laundry/api_wrapper.py b/backend/laundry/api_wrapper.py index 53a99a95..64a86e4f 100644 --- a/backend/laundry/api_wrapper.py +++ b/backend/laundry/api_wrapper.py @@ -1,46 +1,66 @@ import requests -from bs4 import BeautifulSoup from django.conf import settings from django.utils import timezone -from requests.exceptions import ConnectTimeout, HTTPError, ReadTimeout +from requests.exceptions import HTTPError 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): +def get_validated(url): + """ + Makes a request to the given URL and returns the JSON response if the request is successful. + Uses headers specific to the laundry API and should not be used for other requests. + @param url: The URL to make the request to. + @return: The JSON response if the request is successful, otherwise None. + """ + try: + request = requests.get(url, timeout=60, headers=settings.LAUNDRY_HEADERS) + request.raise_for_status() + return request.json() + except HTTPError as e: + print(f"Error: {e}") + return None + + +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 + # TODO: "IN_USE", "AVAILABLE", "COMPLETE"; + # TODO: need to update if we identify other codes, especially error + status = machine["currentStatus"]["statusId"] + if status == "IN_USE": + time_remaining = machine["currentStatus"]["remainingSeconds"] + 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 + # TODO: Verify there are no other statuses 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": []} @@ -48,41 +68,24 @@ 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_json = get_validated(room_request_link) + if request_json is None: 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, - } - ) + for machine in request_json: + if machine["isWasher"]: + update_machine_object(machine, washers) + elif machine["isDryer"]: + update_machine_object(machine, dryers) + detailed = [ + { + "id": machine["id"], + "type": "washer" if machine["isWasher"] else "dryer", + "status": machine["currentStatus"]["statusId"], + "time_remaining": machine["currentStatus"]["remainingSeconds"], + } + for machine in request_json + if machine["isWasher"] or machine["isDryer"] + ] return {"washers": washers, "dryers": dryers, "details": detailed} @@ -92,26 +95,8 @@ def check_is_working(): Returns True if the wash alert web interface seems to be working properly, or False otherwise. """ - 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": "pennappslabs@gmail.com", - "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 - ) - except HTTPError: - return False + all_rooms_request = get_validated(f"{settings.LAUNDRY_URL}/geoBoundaries/5610?raw=true") + return all_rooms_request is not None def all_status(): @@ -120,16 +105,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} @@ -145,7 +130,6 @@ def save_data(): data = all_status() for name, room in data.items(): - laundry_room = LaundryRoom.objects.get(name=name) LaundrySnapshot.objects.create( diff --git a/backend/laundry/data/laundry_data.csv b/backend/laundry/data/laundry_data.csv index d6708b13..a398ac25 100644 --- a/backend/laundry/data/laundry_data.csv +++ b/backend/laundry/data/laundry_data.csv @@ -1,53 +1,54 @@ -0,Bishop White,Quad,5faec7e9-a4aa-47c2-a514-950c03fac460,9,9 -1,Chestnut Butcher,Quad,7dfa4b34-f44a-4a38-a6b9-44cdb968a915,11,11 -2,Class of 1928 Fisher,Quad,e6697dca-d164-4980-8843-ea0a29b1cf49,8,8 -3,Craig,Quad,37d661ce-3e50-4746-ab68-a5c61cd0bd0a,3,3 -4,DuBois,DuBois,3ffa8978-e742-4076-9bcb-4a3e5c0eca92,6,6 -5,English House,KCEH,b655a5be-1287-4ce2-b693-e9c1ae526f38,3,3 -6,Harnwell Floor 02,Harnwell,1c7a9fb3-a938-4756-83c6-42d601d46036,3,3 -7,Harnwell Floor 04,Harnwell,fba67cc0-336e-42f7-9603-c0b8a0e5030c,3,3 -8,Harnwell Floor 06,Harnwell,87195ec7-eb3d-42fd-84aa-d63f4e45e285,3,3 -9,Harnwell Floor 08,Harnwell,1bbb2ff6-d5e6-406d-a3a2-96c7972cceeb,3,3 -10,Harnwell Floor 10,Harnwell,987bf30b-e8e1-4a9e-b842-c9cd8aeafddc,3,3 -11,Harnwell Floor 12,Harnwell,dcb76f10-0137-4783-8604-bece4111b6dd,3,3 -12,Harnwell Floor 14,Harnwell,941b2fcb-2b1b-4afd-8e8e-c100fbcbe0f2,3,3 -13,Harnwell Floor 16,Harnwell,c74b2798-2d09-42a6-b65c-a5834219be59,3,3 -14,Harnwell Floor 18,Harnwell,f30af904-72ad-49f6-aecf-f44c8301fb6b,3,3 -15,Harnwell Floor 20,Harnwell,80a413fd-e0fa-456d-b922-f1576ded1f98,3,3 -16,Harnwell Floor 22,Harnwell,35119e5e-92c0-45fb-bfeb-f2059196f644,3,3 -17,Harnwell Floor 24,Harnwell,5880b051-8216-4cf4-92d6-5c7475f43eea,3,3 -18,Harrison Floor 04,Harrison,447b5682-4c3c-441d-ab49-5f45aee6991f,3,3 -19,Harrison Floor 06,Harrison,f77f7c68-f719-4843-8987-d64dabc0abff,3,3 -20,Harrison Floor 08,Harrison,6561bb14-634f-437d-84fd-a0837ef991e7,3,3 -21,Harrison Floor 10,Harrison,2dd7a63d-7d13-48e5-b038-98054b4f039f,3,3 -22,Harrison Floor 12,Harrison,fdb607c7-63eb-4d55-a312-0c16b682cbe7,3,3 -23,Harrison Floor 14,Harrison,53fdd440-e887-49e1-9ca9-7bb3cb4ab541,3,3 -24,Harrison Floor 16,Harrison,8cedf60a-8f87-4128-89dd-4c75343ca64a,3,3 -25,Harrison Floor 18,Harrison,116a8d6f-045b-47a5-b3f7-af31f4e661eb,3,3 -26,Harrison Floor 20,Harrison,f6a8b303-1302-49e6-be53-c8e345316ed8,3,3 -27,Harrison Floor 22,Harrison,b21c78af-1ebf-418c-a73b-85dc5ff49763,3,3 -28,Harrison Floor 24,Harrison,9b95c471-053c-46ea-bc3b-d23bcad7a3a1,3,3 -29,Hill House,Hill,82a00eb7-f70d-4a4c-9f0a-c2dafa4b67ea,16,16 -30,Magee Amhurst,Quad,f6825dac-5a5a-4e4b-b66f-ea8226cbe78e,12,12 -31,Mayer,Stouffer,6e3531d1-eebd-48b4-ad04-cf5983d42b02,8,8 -32,Morgan,Quad,f249ca9f-ef84-4a35-9477-449b14612057,2,2 -33,Rodin Floor 02,Rodin,7f25802d-31ad-4f80-ba26-d68a3f403aa8,3,3 -34,Rodin Floor 04,Rodin,49e560fb-c1aa-4c98-a88a-cc9564481ec0,3,3 -35,Rodin Floor 06,Rodin,701ce966-aa3c-4063-b3db-548ad89cb643,3,3 -36,Rodin Floor 08,Rodin,4998a8a2-fb86-4900-bcb7-9d7cc6d9b938,3,3 -37,Rodin Floor 10,Rodin,030c81c4-2300-4e8e-ae3a-303397a2e216,3,3 -38,Rodin Floor 12,Rodin,c561f889-5898-41ba-99f5-2e6d4243e4d3,3,3 -39,Rodin Floor 14,Rodin,2d211700-5b59-4c61-8922-991c0f7d7c15,3,3 -40,Rodin Floor 16,Rodin,a10ede1d-044d-4852-87c7-eba7588c2497,3,3 -41,Rodin Floor 18,Rodin,c3d3f9ae-792c-401c-8bd5-8c61fffe2ab1,3,3 -42,Rodin Floor 20,Rodin,e88d3561-dce7-4188-89e7-b72cff7d69d6,3,3 -43,Rodin Floor 22,Rodin,6b7dcd18-fe4e-4dc2-893f-35f0d7939c3c,3,3 -44,Rodin Floor 24,Rodin,18397cd6-202e-4680-b82e-33ccd9ded1a7,3,3 -45,Sansom East,Sansom,ad980c78-bf6d-429a-9a08-1b0899f83d62,16,16 -46,Sansom West,Sansom,d1637690-098b-4eca-b48b-6d137207a38e,16,16 -47,Stouffer Commons,Stouffer,d4848e7d-fdd0-4faa-b6bd-dc152842cf84,6,6 -48,New College House,New College House,14b91b75-563b-4a7f-8b80-4efed338c29b,10,10 -49,Harrison Floor 02,Harrison,78568718-85eb-420b-bc10-77154a685699,3,3 -50,Van Pelt,Gregory,5d9b0588-c987-4d2c-9842-2ce3e9101577,6,6 -51,Class of 1925,Gregory,78f20171-ab32-4650-a1ce-28ace7095790,4,4 -52,Kings Court,KCEH,fdbd6c5f-cb26-486f-86cc-f0b95a7f2a8a,5,6 +room_id,room_name,room_description,room_location,count_washers,count_dryers +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 diff --git a/backend/laundry/management/commands/load_laundry_rooms.py b/backend/laundry/management/commands/load_laundry_rooms.py index 6b26cc8b..65af9c0d 100644 --- a/backend/laundry/management/commands/load_laundry_rooms.py +++ b/backend/laundry/management/commands/load_laundry_rooms.py @@ -7,20 +7,29 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): - + count = 0 with open("laundry/data/laundry_data.csv") as data: - reader = csv.reader(data) - - for i, row in enumerate(reader): - hall_id, hall_name, location, uuid, total_washers, total_dryers = row - + reader = csv.DictReader(data) + for row in reader: LaundryRoom.objects.get_or_create( - hall_id=int(hall_id), - name=hall_name, - location=location, - uuid=uuid, - total_washers=total_washers, - total_dryers=total_dryers, + room_id=int(row["room_id"]), + defaults={ + "name": row["room_name"], + "location": row["room_description"], + "location_id": int(row["room_location"]), + "total_washers": int(row["count_washers"]), + "total_dryers": int(row["count_dryers"]), + }, ) + count += 1 self.stdout.write("Uploaded Laundry Rooms!") + ( + self.stdout.write( + f"Warning: There are {LaundryRoom.objects.all().count() - count} rooms in the " + f"database but not in the data file. If they are no longer supported by Penn's " + f"servers, consider deleting them." + ) + if count < LaundryRoom.objects.all().count() + else None + ) diff --git a/backend/laundry/management/commands/update_laundry_rooms.py b/backend/laundry/management/commands/update_laundry_rooms.py new file mode 100644 index 00000000..a2be0195 --- /dev/null +++ b/backend/laundry/management/commands/update_laundry_rooms.py @@ -0,0 +1,55 @@ +import csv + +from django.conf import settings +from django.core.management.base import BaseCommand + +from laundry.api_wrapper import get_validated + + +def write_file(laundry_rooms): + with open("laundry/data/laundry_data.csv", "w") as f: + writer = csv.DictWriter(f, laundry_rooms[0].keys()) + writer.writeheader() + writer.writerows(laundry_rooms) + + +class Command(BaseCommand): + help = "Update laundry rooms csv from server" + + def handle(self, *args, **kwargs): + # Pull initial request with everything + all_rooms_request_json = get_validated( + f"{settings.LAUNDRY_URL}/geoBoundaries/5610?raw=true" + ) + if all_rooms_request_json is None: + return + locations = all_rooms_request_json["geoBoundaries"][0]["geoBoundaries"] + + laundry_rooms = [ + { + "room_id": int(room["id"]), + "room_name": room["roomName"], + "room_description": location["description"], + "room_location": int(location["id"]), + } + 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 to update + for room in laundry_rooms: + room_request_json = get_validated( + f"{settings.LAUNDRY_URL}/rooms/{room['room_id']}/machines?raw=true" + ) + if room_request_json is None: + return + # count washers and dryers + room["count_washers"] = len( + [machine for machine in room_request_json if machine["isWasher"]] + ) + room["count_dryers"] = len( + [machine for machine in room_request_json if machine["isDryer"]] + ) + + write_file(laundry_rooms) diff --git a/backend/laundry/migrations/0003_rename_hall_id_laundryroom_room_id_and_more.py b/backend/laundry/migrations/0003_rename_hall_id_laundryroom_room_id_and_more.py new file mode 100644 index 00000000..522066ad --- /dev/null +++ b/backend/laundry/migrations/0003_rename_hall_id_laundryroom_room_id_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.2 on 2024-11-17 18:04 + +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), + ), + ] diff --git a/backend/laundry/migrations/0004_alter_laundryroom_room_id.py b/backend/laundry/migrations/0004_alter_laundryroom_room_id.py new file mode 100644 index 00000000..93e950c4 --- /dev/null +++ b/backend/laundry/migrations/0004_alter_laundryroom_room_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-11-17 18:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("laundry", "0003_rename_hall_id_laundryroom_room_id_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="laundryroom", + name="room_id", + field=models.IntegerField(default=0, unique=True), + ), + ] diff --git a/backend/laundry/models.py b/backend/laundry/models.py index 4af9bd2a..cd52d185 100644 --- a/backend/laundry/models.py +++ b/backend/laundry/models.py @@ -1,22 +1,17 @@ -import uuid - from django.db import models from django.utils import timezone class LaundryRoom(models.Model): - hall_id = models.IntegerField(default=0) + room_id = models.IntegerField(default=0, unique=True) name = models.CharField(max_length=255) location = models.CharField(max_length=255) + location_id = models.IntegerField(default=0) total_washers = models.IntegerField(default=0) total_dryers = models.IntegerField(default=0) - # Each Laundry Room has a UUID that we need to - # access Penn API laundry data - uuid = models.UUIDField(default=uuid.uuid4) - def __str__(self): - return f"Hall No. {self.hall_id} | {self.name}" + return f"Room {self.name} | {self.location}" class LaundrySnapshot(models.Model): @@ -26,4 +21,4 @@ class LaundrySnapshot(models.Model): available_dryers = models.IntegerField() def __str__(self): - return f"Hall No. {self.room.hall_id} | {self.date.date()}" + return f"Room {self.room.name} | {self.date.date()}" diff --git a/backend/laundry/serializers.py b/backend/laundry/serializers.py index fb35d9e9..59581969 100644 --- a/backend/laundry/serializers.py +++ b/backend/laundry/serializers.py @@ -4,6 +4,9 @@ class LaundryRoomSerializer(serializers.ModelSerializer): + # tech debt but we're rewriting at some point and this maintains frontend-compatibility + hall_id = serializers.IntegerField(source="room_id") + class Meta: model = LaundryRoom fields = ("name", "hall_id", "location") diff --git a/backend/laundry/urls.py b/backend/laundry/urls.py index b05861d1..0fa26807 100644 --- a/backend/laundry/urls.py +++ b/backend/laundry/urls.py @@ -6,10 +6,10 @@ urlpatterns = [ - path("hall//", cache_page(Cache.MINUTE)(HallInfo.as_view()), name="hall-info"), - path("usage//", cache_page(Cache.MINUTE)(HallUsage.as_view()), name="hall-usage"), + path("hall//", cache_page(Cache.MINUTE)(HallInfo.as_view()), name="hall-info"), + path("usage//", cache_page(Cache.MINUTE)(HallUsage.as_view()), name="hall-usage"), path( - "rooms/", + "rooms/", cache_page(Cache.MINUTE)(MultipleHallInfo.as_view()), name="multiple-hall-info", ), diff --git a/backend/laundry/views.py b/backend/laundry/views.py index fa9a008f..781ed4b8 100644 --- a/backend/laundry/views.py +++ b/backend/laundry/views.py @@ -10,7 +10,7 @@ from rest_framework.response import Response from rest_framework.views import APIView -from laundry.api_wrapper import check_is_working, hall_status +from laundry.api_wrapper import check_is_working, room_status from laundry.models import LaundryRoom, LaundrySnapshot from laundry.serializers import LaundryRoomSerializer from pennmobile.analytics import Metric, record_analytics @@ -31,9 +31,9 @@ class HallInfo(APIView): GET: returns list of a particular hall, its respective machines and machine details """ - def get(self, request, hall_id): + def get(self, request, room_id): try: - return Response(hall_status(get_object_or_404(LaundryRoom, hall_id=hall_id))) + return Response(room_status(get_object_or_404(LaundryRoom, room_id=room_id))) except HTTPError: return Response({"error": "The laundry api is currently unavailable."}, status=503) @@ -43,15 +43,15 @@ class MultipleHallInfo(APIView): GET: returns list of hall information as well as hall usage """ - def get(self, request, hall_ids): - halls = [int(x) for x in hall_ids.split(",")] + def get(self, request, room_ids): + rooms = [int(x) for x in room_ids.split(",")] output = {"rooms": []} - for hall_id in halls: - hall_data = hall_status(get_object_or_404(LaundryRoom, hall_id=hall_id)) - hall_data["id"] = hall_id - hall_data["usage_data"] = HallUsage.compute_usage(hall_id) - output["rooms"].append(hall_data) + for room_id in rooms: + room_data = room_status(get_object_or_404(LaundryRoom, room_id=room_id)) + room_data["id"] = room_id + room_data["usage_data"] = HallUsage.compute_usage(room_id) + output["rooms"].append(room_id) record_analytics(Metric.LAUNDRY_VIEWED, request.user.username) @@ -66,9 +66,9 @@ class HallUsage(APIView): def safe_division(a, b): return round(a / float(b), 3) if b > 0 else 0 - def get_snapshot_info(hall_id): + def get_snapshot_info(room_id): # filters for LaundrySnapshots within timeframe - room = get_object_or_404(LaundryRoom, hall_id=hall_id) + room = get_object_or_404(LaundryRoom, room_id=room_id) # get start time, which is now without the times start = timezone.localtime().replace(hour=0, minute=0, second=0, microsecond=0) @@ -83,9 +83,9 @@ def get_snapshot_info(hall_id): snapshots = LaundrySnapshot.objects.filter(filter).order_by("-date") return (room, snapshots) - def compute_usage(hall_id): + def compute_usage(room_id): try: - (room, snapshots) = HallUsage.get_snapshot_info(hall_id) + (room, snapshots) = HallUsage.get_snapshot_info(room_id) except ValueError: return Response({"error": "Invalid hall id passed to server."}, status=404) @@ -135,8 +135,8 @@ def compute_usage(hall_id): return content - def get(self, request, hall_id): - return Response(HallUsage.compute_usage(hall_id)) + def get(self, request, room_id): + return Response(HallUsage.compute_usage(room_id)) class Preferences(APIView): @@ -155,7 +155,10 @@ def get(self, request): cached_preferences = cache.get(key) if cached_preferences is None: preferences = request.user.profile.laundry_preferences.all() - cached_preferences = preferences.values_list("hall_id", flat=True) + cached_preferences = preferences.values_list("room_id", flat=True) + # get all laundries with one of these + valid_rooms = LaundryRoom.objects.filter(room_id__in=cached_preferences) + cached_preferences = valid_rooms.values_list("room_id", flat=True) cache.set(key, cached_preferences, Cache.MONTH) return Response({"rooms": cached_preferences}) @@ -166,12 +169,10 @@ def post(self, request): preferences = profile.laundry_preferences if "rooms" not in request.data: return Response({"success": False, "error": "No rooms provided"}, status=400) - halls = [ - get_object_or_404(LaundryRoom, hall_id=int(hall_id)) - for hall_id in request.data["rooms"] + get_object_or_404(LaundryRoom, room_id=int(room_id)) + for room_id in request.data["rooms"] ] - # clears all previous preferences in many-to-many preferences.clear() preferences.add(*halls) diff --git a/backend/penndata/views.py b/backend/penndata/views.py index fba31b18..d8463729 100644 --- a/backend/penndata/views.py +++ b/backend/penndata/views.py @@ -11,6 +11,7 @@ from rest_framework.response import Response from rest_framework.views import APIView +from laundry.models import LaundryRoom from penndata.models import ( AnalyticsEvent, CalendarEvent, @@ -174,12 +175,15 @@ def get(self, request): cells = [] - # adds laundry preference to home, defaults to Bishop if no preference + # adds laundry preference to home, defaults to 0 if no preference + # TODO: This defaults to the first room, change potentially laundry_preference = profile.laundry_preferences.first() if laundry_preference: - cells.append(self.Cell("laundry", {"room_id": laundry_preference.hall_id}, 5)) + cells.append(self.Cell("laundry", {"room_id": laundry_preference.room_id}, 5)) else: - cells.append(self.Cell("laundry", {"room_id": 0}, 5)) + cells.append( + self.Cell("laundry", {"room_id": list(LaundryRoom.objects.all())[0].room_id}, 5) + ) # adds dining preference to home with high priority, defaults to 1920's, Hill, NCH dining_preferences = [ diff --git a/backend/pennmobile/settings/base.py b/backend/pennmobile/settings/base.py index c1bcd22a..e2bc3154 100644 --- a/backend/pennmobile/settings/base.py +++ b/backend/pennmobile/settings/base.py @@ -161,7 +161,13 @@ # Laundry API URL # LAUNDRY_URL = os.environ.get("LAUNDRY_URL", "http://suds.kite.upenn.edu") -LAUNDRY_URL = "http://laundry.sketchy.dev/" +LAUNDRY_URL = "https://api.alliancelslabs.com" +LAUNDRY_X_API_KEY = os.environ.get("LAUNDRY_X_API_KEY", None) +LAUNDRY_ALLIANCELS_API_KEY = os.environ.get("LAUNDRY_ALLIANCE_LS_KEY", None) +LAUNDRY_HEADERS = { + "x-api-key": LAUNDRY_X_API_KEY, + "alliancels-auth-token": LAUNDRY_ALLIANCELS_API_KEY, +} # Dining API Credentials DINING_USERNAME = os.environ.get("DINING_USERNAME", None) diff --git a/backend/tests/laundry/mock_geoboundaries_request.json b/backend/tests/laundry/mock_geoboundaries_request.json new file mode 100644 index 00000000..14dd62bf --- /dev/null +++ b/backend/tests/laundry/mock_geoboundaries_request.json @@ -0,0 +1,225 @@ +{ + "id": "481806", + "organizationName": "University of Pennsylvania", + "merchantName": null, + "userGuid": "c7970c4c-5e73-4533-82ab-b25a0fcad7d3", + "userRole": { + "id": "485807", + "roleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + } + }, + "weightLookupKey": "POUND", + "isAtrium": false, + "isMeteredLaundry": false, + "isIncludedLaundry": true, + "atriumBalanceName": "", + "meteredBalanceName": "", + "geoBoundaries": [ + { + "fundsRestriction": null, + "hasCard": null, + "washAlertScaleX": null, + "washAlertScaleY": null, + "description": "University of Pennsylvania", + "id": "5610", + "isDup": false, + "geoBoundaryType": { + "id": "LOCATION" + }, + "breadcrumbIdArray": [ + "5610" + ], + "breadcrumbDisplayArray": [ + "University of Pennsylvania" + ], + "geoBoundaries": [ + { + "fundsRestriction": null, + "hasCard": null, + "washAlertScaleX": null, + "washAlertScaleY": null, + "description": "English House", + "id": "14146", + "isDup": false, + "geoBoundaryType": { + "id": "LOCATION" + }, + "breadcrumbIdArray": [ + "5610", + "14146" + ], + "breadcrumbDisplayArray": [ + "University of Pennsylvania", + "English House" + ], + "latitude": "39.9522188", + "longitude": "-75.1932137", + "rooms": [ + { + "id": "14089", + "roomName": "English House", + "breadcrumbDisplay": "University of Pennsylvania/English House/English House", + "breadcrumbId": "5610/14146/14089", + "isActive": true, + "createdAt": "2024-06-20T14:42:08.214Z", + "createdBy": "74297e47-3119-4037-9573-5ed1689788aa", + "updatedAt": "2024-06-20T14:42:08.214Z", + "updatedBy": null, + "geomName": "201 S 40th St, Philadelphia, PA 19104", + "latitude": "39.9522188", + "longitude": "-75.1932137", + "timeZoneName": "America/New_York", + "internationalTimeZoneName": "America/New_York", + "internationalTimeZoneAbbreviation": "EDT", + "hasCoin": true, + "hasVtm": false, + "hasWashAlert": true, + "washAlertImageUrl": null, + "washAlertXAxis": null, + "washAlertYAxis": null, + "canSms": true, + "serviceEmail": "campus-service@caleco.net", + "servicePhoneNumber": null, + "roomBanner": null, + "advancedGateway": true, + "connectionType": "CONNECTED", + "subscriptionExpired": false, + "locationPin": "QUAKER", + "signupBonusAmount": null, + "signupBonusExpirationDate": null, + "maxRedemptions": null, + "ordinalValue": 0 + } + ], + "ordinalValue": 0, + "isRetail": false + }, + { + "fundsRestriction": null, + "hasCard": null, + "washAlertScaleX": null, + "washAlertScaleY": null, + "description": "Harnwell College House", + "id": "14150", + "isDup": true, + "geoBoundaryType": { + "id": "LOCATION" + }, + "breadcrumbIdArray": [ + "5610", + "14150" + ], + "breadcrumbDisplayArray": [ + "University of Pennsylvania", + "Harnwell College House" + ], + "latitude": "39.9522188", + "longitude": "-75.1932137", + "rooms": [ + { + "id": "14099", + "roomName": "Harnwell 10th Floor", + "breadcrumbDisplay": "University of Pennsylvania/Harnwell College House/Harnwell 10th Floor", + "breadcrumbId": "5610/14150/14099", + "isActive": true, + "createdAt": "2024-06-20T15:53:02.144Z", + "createdBy": "74297e47-3119-4037-9573-5ed1689788aa", + "updatedAt": "2024-06-20T15:53:02.144Z", + "updatedBy": null, + "geomName": "201 S 40th St, Philadelphia, PA 19104", + "latitude": "39.9522188", + "longitude": "-75.1932137", + "timeZoneName": "America/New_York", + "internationalTimeZoneName": "America/New_York", + "internationalTimeZoneAbbreviation": "EDT", + "hasCoin": true, + "hasVtm": false, + "hasWashAlert": true, + "washAlertImageUrl": null, + "washAlertXAxis": null, + "washAlertYAxis": null, + "canSms": true, + "serviceEmail": "campus-service@caleco.net", + "servicePhoneNumber": null, + "roomBanner": null, + "advancedGateway": true, + "connectionType": "CONNECTED", + "subscriptionExpired": false, + "locationPin": "QUAKER", + "signupBonusAmount": null, + "signupBonusExpirationDate": null, + "maxRedemptions": null, + "ordinalValue": 0 + }, + { + "id": "14100", + "roomName": "Harnwell 12th Floor", + "breadcrumbDisplay": "University of Pennsylvania/Harnwell College House/Harnwell 12th Floor", + "breadcrumbId": "5610/14150/14100", + "isActive": true, + "createdAt": "2024-06-20T16:15:21.671Z", + "createdBy": "74297e47-3119-4037-9573-5ed1689788aa", + "updatedAt": "2024-06-20T16:15:21.671Z", + "updatedBy": null, + "geomName": "201 S 40th St, Philadelphia, PA 19104", + "latitude": "39.9522188", + "longitude": "-75.1932137", + "timeZoneName": "America/New_York", + "internationalTimeZoneName": "America/New_York", + "internationalTimeZoneAbbreviation": "EDT", + "hasCoin": true, + "hasVtm": false, + "hasWashAlert": true, + "washAlertImageUrl": null, + "washAlertXAxis": null, + "washAlertYAxis": null, + "canSms": true, + "serviceEmail": "campus-service@caleco.net", + "servicePhoneNumber": null, + "roomBanner": null, + "advancedGateway": true, + "connectionType": "CONNECTED", + "subscriptionExpired": false, + "locationPin": "QUAKER", + "signupBonusAmount": null, + "signupBonusExpirationDate": null, + "maxRedemptions": null, + "ordinalValue": 1 + } + ], + "ordinalValue": 4, + "isRetail": false + } + ], + "ordinalValue": 0, + "isRetail": false + } + ], + "currency": { + "name": "US Dollars", + "divisor": 100, + "decimalPlaces": 2, + "iso4217Code": "USD", + "iso4217Id": "840", + "id": "840", + "symbol": "$", + "thousandsSeparator": ",", + "decimalSeparator": ".", + "overrideDecimalPlaces": null + }, + "country": { + "id": "1", + "name": "United States", + "isoCountryName": "United States of America", + "isoCountryCode": "US" + }, + "auxFundsType": null, + "hasCvvVerification": false, + "hasAddressVerification": false, + "isRetail": false, + "isCrm": false, + "isTrade": false, + "hasMailbox": false, + "isoStartDayOfWeek": "7" +} \ No newline at end of file diff --git a/backend/tests/laundry/mock_rooms_request_14089.json b/backend/tests/laundry/mock_rooms_request_14089.json new file mode 100644 index 00000000..63f36c67 --- /dev/null +++ b/backend/tests/laundry/mock_rooms_request_14089.json @@ -0,0 +1,505 @@ +[ + { + "id": "442890", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "176099620618346500", + "createdAt": "2024-07-24T15:07:22.001628", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:22.001628", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 4", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "4", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2405022776", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0004", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "176099620618346500", + "createdAt": "2024-10-27T16:54:02.706000", + "displayStatus": { + "id": "0", + "times": [ + 1000, + 1000 + ], + "values": [ + "CLo5E", + "door" + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442890", + "isDoorOpen": false, + "linkQualityIndicator": 65, + "microType": 0, + "receivedAt": "2024-10-27T16:54:02.706000", + "remainingSeconds": 3600, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "AVAILABLE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "52f7bf39-56c8-4d2e-b946-f9fadc8fbb20" + }, + "ordinalValue": 0 + }, + { + "id": "442888", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "143031774052786180", + "createdAt": "2024-07-24T15:07:17.440014", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:17.440014", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 1", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "1", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2310023277", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0001", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "143031774052786180", + "createdAt": "2024-10-27T17:01:01.079000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + "oPEn " + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442888", + "isDoorOpen": false, + "linkQualityIndicator": 53, + "microType": 0, + "receivedAt": "2024-10-27T17:01:01.079000", + "remainingSeconds": 1800, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 2, + "name": "NORMAL_WARM" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "COMPLETE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "95599a7d-1fe6-4729-ae1a-4be79cfdcaf8" + }, + "ordinalValue": 1 + }, + { + "id": "442886", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "142594022396043268", + "createdAt": "2024-07-24T15:07:16.013735", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:16.013735", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 2", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "2", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2310045791", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0002", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "142594022396043268", + "createdAt": "2024-10-27T17:03:04.917000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :18" + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442886", + "isDoorOpen": false, + "linkQualityIndicator": 53, + "microType": 0, + "receivedAt": "2024-10-27T17:03:04.917000", + "remainingSeconds": 1028, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 3, + "name": "NORMAL_COLD" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "IN_USE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "abf455ce-c829-4076-b4b6-90607f376551" + }, + "ordinalValue": 2 + }, + { + "id": "442885", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "142593846302384132", + "createdAt": "2024-07-24T15:07:15.014176", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:15.014176", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 3", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "3", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2310045788", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0003", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "142593846302384132", + "createdAt": "2024-10-27T17:03:05.920000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " 03" + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442885", + "isDoorOpen": false, + "linkQualityIndicator": 64, + "microType": 0, + "receivedAt": "2024-10-27T17:03:05.920000", + "remainingSeconds": 138, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 2, + "name": "NORMAL_WARM" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "IN_USE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "244ae682-041b-451e-9767-d6bda3556a0b" + }, + "ordinalValue": 3 + }, + { + "id": "442887", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "177830445193994244", + "createdAt": "2024-07-24T15:07:16.548421", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:16.548421", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 5", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "5", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2405015200", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0005", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "177830445193994244", + "createdAt": "2024-10-27T17:03:07.675000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :44" + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442887", + "isDoorOpen": false, + "linkQualityIndicator": 58, + "microType": 0, + "receivedAt": "2024-10-27T17:03:07.675000", + "remainingSeconds": 2604, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 1, + "name": "HIGH" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "IN_USE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "8a94d44d-fd31-4545-b6b3-e118b0c8302e" + }, + "ordinalValue": 4 + }, + { + "id": "442889", + "room": { + "id": "14089", + "roomName": "English House" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "177830470963798020", + "createdAt": "2024-07-24T15:07:21.831619", + "createdBy": 2, + "updatedAt": "2024-07-24T15:07:21.831619", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 6", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "6", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2405015201", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0006", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "177830470963798020", + "createdAt": "2024-10-27T17:03:04.184000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " 18" + ] + }, + "gatewayName": "37e5b85195ad97b00f7e45eb07c0b8ee", + "id": "442889", + "isDoorOpen": false, + "linkQualityIndicator": 56, + "microType": 0, + "receivedAt": "2024-10-27T17:03:04.184000", + "remainingSeconds": 1044, + "remainingVend": 0, + "roomId": "14089", + "selectedCycle": { + "id": 1, + "name": "HIGH" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "IN_USE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "609573cd-6704-48a5-8616-d4e07c02234b" + }, + "ordinalValue": 5 + } +] \ No newline at end of file diff --git a/backend/tests/laundry/mock_rooms_request_14099.json b/backend/tests/laundry/mock_rooms_request_14099.json new file mode 100644 index 00000000..e21235f2 --- /dev/null +++ b/backend/tests/laundry/mock_rooms_request_14099.json @@ -0,0 +1,507 @@ +[ + { + "id": "452710", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "148190089904824324", + "createdAt": "2024-07-30T17:42:08.184344", + "createdBy": 2, + "updatedAt": "2024-07-30T17:42:08.184344", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 28", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "28", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2403015356", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0028", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "148190089904824324", + "createdAt": "2024-10-27T17:01:06.186000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + "5tArt " + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452710", + "isDoorOpen": false, + "linkQualityIndicator": 59, + "microType": 0, + "receivedAt": "2024-10-27T17:01:06.186000", + "remainingSeconds": 1800, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 3, + "name": "NORMAL_COLD" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "AVAILABLE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "479c5648-a406-47f7-9c0e-56e424ada3d8" + }, + "ordinalValue": 0 + }, + { + "id": "452707", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "172669286073606148", + "createdAt": "2024-07-30T17:42:03.25572", + "createdBy": 2, + "updatedAt": "2024-07-30T17:42:03.25572", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 27", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "27", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2403050733", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0027", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "172669286073606148", + "createdAt": "2024-10-27T16:49:04.122000", + "displayStatus": { + "id": "0", + "times": [ + 1000, + 1000 + ], + "values": [ + "CLo5E", + "door" + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452707", + "isDoorOpen": false, + "linkQualityIndicator": 43, + "microType": 0, + "receivedAt": "2024-10-27T16:49:04.122000", + "remainingSeconds": 3600, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "AVAILABLE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "be4e14d9-127f-48e0-866c-577fd09dafaa" + }, + "ordinalValue": 1 + }, + { + "id": "452711", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "173953833481969668", + "createdAt": "2024-07-30T17:42:14.343464", + "createdBy": 2, + "updatedAt": "2024-07-30T17:42:14.343464", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 29", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "29", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2404029726", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0029", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "173953833481969668", + "createdAt": "2024-10-27T17:04:02.815000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :15" + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452711", + "isDoorOpen": false, + "linkQualityIndicator": 65, + "microType": 0, + "receivedAt": "2024-10-27T17:04:02.815000", + "remainingSeconds": 868, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 7, + "name": "DELICATE_HOT" + }, + "selectedModifier": { + "id": 2, + "name": "HEAVY" + }, + "statusId": "IN_USE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "8eb1203b-ae9e-4954-8acf-f5c8efb1aff9" + }, + "ordinalValue": 2 + }, + { + "id": "452708", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "148190094199791620", + "createdAt": "2024-07-30T17:42:05.262754", + "createdBy": 2, + "updatedAt": "2024-07-30T17:42:05.262754", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 30", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "30", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2403015371", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0030", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "148190094199791620", + "createdAt": "2024-10-27T17:03:03.840000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :16" + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452708", + "isDoorOpen": false, + "linkQualityIndicator": 51, + "microType": 0, + "receivedAt": "2024-10-27T17:03:03.840000", + "remainingSeconds": 944, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 7, + "name": "DELICATE_HOT" + }, + "selectedModifier": { + "id": 2, + "name": "HEAVY" + }, + "statusId": "IN_USE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "3eb22a05-e2fc-426a-b297-459de85c98d1" + }, + "ordinalValue": 3 + }, + { + "id": "452706", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "172669247418900484", + "createdAt": "2024-07-30T17:41:58.981704", + "createdBy": 2, + "updatedAt": "2024-07-30T17:41:58.981704", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 25", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "25", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2403050789", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0025", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "172669247418900484", + "createdAt": "2024-10-27T17:01:03.045000", + "displayStatus": { + "id": "0", + "times": [ + 1000, + 1000 + ], + "values": [ + "CLo5E", + "door" + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452706", + "isDoorOpen": false, + "linkQualityIndicator": 60, + "microType": 0, + "receivedAt": "2024-10-27T17:01:03.045000", + "remainingSeconds": 0, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "COMPLETE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "4e69aab9-0236-4e6d-b4e2-01ba6e9fdb34" + }, + "ordinalValue": 4 + }, + { + "id": "452709", + "room": { + "id": "14099", + "roomName": "Harnwell 10th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "172670157951967236", + "createdAt": "2024-07-30T17:42:08.162923", + "createdBy": 2, + "updatedAt": "2024-07-30T17:42:08.162923", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 26", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "26", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2403045943", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0026", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "172670157951967236", + "createdAt": "2024-10-27T17:04:05.699000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :24" + ] + }, + "gatewayName": "334b35ddd2700be0075c5af0aa247aa9", + "id": "452709", + "isDoorOpen": false, + "linkQualityIndicator": 43, + "microType": 0, + "receivedAt": "2024-10-27T17:04:05.699000", + "remainingSeconds": 1386, + "remainingVend": 0, + "roomId": "14099", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "IN_USE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "8e510997-b964-4c02-b6f8-61b39536e2bb" + }, + "ordinalValue": 5 + } +] \ No newline at end of file diff --git a/backend/tests/laundry/mock_rooms_request_14100.json b/backend/tests/laundry/mock_rooms_request_14100.json new file mode 100644 index 00000000..c96b9089 --- /dev/null +++ b/backend/tests/laundry/mock_rooms_request_14100.json @@ -0,0 +1,505 @@ +[ + { + "id": "452757", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "148624543026683908", + "createdAt": "2024-07-30T17:52:12.776571", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:12.776571", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 35", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "35", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2402034894", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0035", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "148624543026683908", + "createdAt": "2024-10-27T16:42:01.195000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + "5tArt " + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452757", + "isDoorOpen": false, + "linkQualityIndicator": 44, + "microType": 0, + "receivedAt": "2024-10-27T16:42:01.195000", + "remainingSeconds": 1800, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 3, + "name": "NORMAL_COLD" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "AVAILABLE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "a56bad9d-935d-485c-a149-155de0a20139" + }, + "ordinalValue": 0 + }, + { + "id": "452756", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "130156424172511236", + "createdAt": "2024-07-30T17:52:10.442788", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:10.442788", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 31", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "31", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2303052978", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0031", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "130156424172511236", + "createdAt": "2024-10-27T17:04:05.650000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + "5tArt " + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452756", + "isDoorOpen": false, + "linkQualityIndicator": 49, + "microType": 0, + "receivedAt": "2024-10-27T17:04:05.650000", + "remainingSeconds": 3600, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "AVAILABLE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "02f531bc-dea3-48dd-a42c-e15916940e63" + }, + "ordinalValue": 1 + }, + { + "id": "452766", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "130155998970748932", + "createdAt": "2024-07-30T17:52:18.485909", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:18.485909", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 32", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "32", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2303052988", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0032", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "130155998970748932", + "createdAt": "2024-10-27T17:05:04.327000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + "FrEE " + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452766", + "isDoorOpen": false, + "linkQualityIndicator": 56, + "microType": 0, + "receivedAt": "2024-10-27T17:05:04.327000", + "remainingSeconds": 3600, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "AVAILABLE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "d423bd7f-60e0-47e2-b2fd-6c00770d7737" + }, + "ordinalValue": 2 + }, + { + "id": "452763", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": true, + "isActive": true, + "isWasher": false, + "controlId": "130156978223292420", + "createdAt": "2024-07-30T17:52:17.41128", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:17.41128", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 33", + "machineType": { + "id": "20", + "isDryer": true, + "isWasher": false, + "typeName": "Dryer" + }, + "modelNumber": "SDENYRGS176TW01", + "networkNode": "33", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2303053001", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0033", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": true, + "controlId": "130156978223292420", + "createdAt": "2024-10-27T16:50:15.737000", + "displayStatus": { + "id": "0", + "times": [ + 1000, + 1000 + ], + "values": [ + "CLo5E", + "door" + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452763", + "isDoorOpen": false, + "linkQualityIndicator": 45, + "microType": 0, + "receivedAt": "2024-10-27T16:50:15.737000", + "remainingSeconds": 3600, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 4, + "name": "MEDIUM" + }, + "selectedModifier": { + "id": 0 + }, + "statusId": "AVAILABLE", + "topOffTime": 300, + "topOffVend": 25, + "topoffFullyDisabled": false, + "uuid": "b9955d3e-0b76-4f32-a81f-ce83c005e86c" + }, + "ordinalValue": 3 + }, + { + "id": "452754", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "175240335100911620", + "createdAt": "2024-07-30T17:52:04.528356", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:04.528356", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 34", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "34", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2405043743", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0034", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "175240335100911620", + "createdAt": "2024-10-27T17:05:03.097000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " 21" + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452754", + "isDoorOpen": false, + "linkQualityIndicator": 41, + "microType": 0, + "receivedAt": "2024-10-27T17:05:03.097000", + "remainingSeconds": 1223, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 3, + "name": "NORMAL_COLD" + }, + "selectedModifier": { + "id": 2, + "name": "HEAVY" + }, + "statusId": "IN_USE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "5383d36f-042e-4e6c-825d-7c9140dd065e" + }, + "ordinalValue": 4 + }, + { + "id": "452755", + "room": { + "id": "14100", + "roomName": "Harnwell 12th Floor" + }, + "isDryer": false, + "isActive": true, + "isWasher": true, + "controlId": "175240266381434884", + "createdAt": "2024-07-30T17:52:10.297965", + "createdBy": 2, + "updatedAt": "2024-07-30T17:52:10.297965", + "updatedBy": null, + "isExternal": false, + "machineName": "Machine Node - 36", + "machineType": { + "id": "10", + "isDryer": false, + "isWasher": true, + "typeName": "Frontload Washer" + }, + "modelNumber": "SFNNYRSP116TW01", + "networkNode": "36", + "organization": { + "id": "481806", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "organizationName": "University of Pennsylvania" + }, + "serialNumber": "2405043736", + "userRoleType": { + "id": "ORGANIZATION_ROUTE_UNIVERSITY" + }, + "machineNumber": "0036", + "machineAuditType": { + "id": "TITAN_T4", + "typeName": "TITAN_T4" + }, + "machineGeneration": null, + "networkController": null, + "machineAuditGroupType": { + "id": "TITAN", + "typeName": "Quantum Gold Pro" + }, + "currentStatus": { + "canTopOff": false, + "controlId": "175240266381434884", + "createdAt": "2024-10-27T17:02:07.346000", + "displayStatus": { + "id": "0", + "times": [ + 1000 + ], + "values": [ + " :00" + ] + }, + "gatewayName": "3e9913389cde1a4518a93c2786748328", + "id": "452755", + "isDoorOpen": false, + "linkQualityIndicator": 42, + "microType": 0, + "receivedAt": "2024-10-27T17:02:07.346000", + "remainingSeconds": 1800, + "remainingVend": 0, + "roomId": "14100", + "selectedCycle": { + "id": 3, + "name": "NORMAL_COLD" + }, + "selectedModifier": { + "id": 0, + "name": "LIGHT" + }, + "statusId": "COMPLETE", + "topOffTime": 0, + "topOffVend": 0, + "topoffFullyDisabled": true, + "uuid": "f8556ec4-fe2a-47c4-b74b-97045e019165" + }, + "ordinalValue": 5 + } +] \ No newline at end of file diff --git a/backend/tests/laundry/test_api_wrapper.py b/backend/tests/laundry/test_api_wrapper.py index dccca9ea..bb5b8c11 100644 --- a/backend/tests/laundry/test_api_wrapper.py +++ b/backend/tests/laundry/test_api_wrapper.py @@ -1,37 +1,45 @@ from unittest import mock -from django.conf import settings from django.test import TestCase -from laundry.api_wrapper import all_status, hall_status, save_data +from laundry.api_wrapper import all_status, room_status, save_data from laundry.models import LaundryRoom, LaundrySnapshot -from tests.laundry.test_commands import fakeLaundryGet +from tests.laundry.test_commands import mock_laundry_get -ALL_URL = f"{settings.LAUNDRY_URL}/?location=" - - -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class TestAllStatus(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) def test_all_status(self): data = all_status() - self.assertEqual(len(data), 4) + self.assertEqual(len(data), 3) for room in LaundryRoom.objects.all(): self.assertIn(room.name, data) @@ -54,20 +62,32 @@ def test_all_status(self): self.assertTrue(hall[machine]["offline"] >= 0) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class TestHallStatus(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 - ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) def test_all_status(self): @@ -75,7 +95,7 @@ def test_all_status(self): for room in LaundryRoom.objects.all(): # asserts fields are present - status = hall_status(room) + status = room_status(room) machines = status["machines"] self.assertIn("washers", machines) self.assertIn("dryers", machines) @@ -86,20 +106,32 @@ def test_all_status(self): self.assertIn("status", machine) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class TestSaveData(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) def test_save_data(self): @@ -109,20 +141,20 @@ def test_save_data(self): save_data() # checks that all rooms have been accounted for - self.assertEqual(LaundrySnapshot.objects.all().count(), 4) + self.assertEqual(LaundrySnapshot.objects.all().count(), 3) # # adds all id's to a list - hall_ids = LaundryRoom.objects.all().values_list("hall_id", flat=True) + room_ids = LaundryRoom.objects.all().values_list("room_id", flat=True) - self.assertEqual(len(hall_ids), 4) + self.assertEqual(len(room_ids), 3) # asserts that fields are correct, and that all snapshots # have been accounted for for snapshot in LaundrySnapshot.objects.all(): - self.assertIn(snapshot.room.hall_id, hall_ids) + self.assertIn(snapshot.room.room_id, room_ids) self.assertTrue(snapshot.available_washers >= 0) self.assertTrue(snapshot.available_dryers >= 0) save_data() - self.assertEqual(LaundrySnapshot.objects.all().count(), 8) + self.assertEqual(LaundrySnapshot.objects.all().count(), 6) diff --git a/backend/tests/laundry/test_commands.py b/backend/tests/laundry/test_commands.py index 79028394..dd7dd602 100644 --- a/backend/tests/laundry/test_commands.py +++ b/backend/tests/laundry/test_commands.py @@ -1,4 +1,5 @@ import csv +import json from io import StringIO from unittest import mock @@ -9,30 +10,49 @@ from laundry.models import LaundryRoom, LaundrySnapshot -def fakeLaundryGet(url, *args, **kwargs): - if settings.LAUNDRY_URL in url: - with open("tests/laundry/laundry_snapshot.html", "rb") as f: - m = mock.MagicMock(content=f.read()) - return m - else: - raise NotImplementedError +def mock_laundry_get(url, *args, **kwargs): + # TODO: should we do this with regex? using split bc I think it's cleaner + split = url.split("/") + if "/".join(split[0:3]) == settings.LAUNDRY_URL: + if split[3] == "rooms": + with open(f"tests/laundry/mock_rooms_request_{split[4]}.json", "rb") as f: + return json.load(f) + elif split[3] == "geoBoundaries": + with open("tests/laundry/mock_geoboundaries_request.json", "rb") as f: + return json.load(f) + else: + raise NotImplementedError -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class TestGetSnapshot(TestCase): def setUp(self): # populates database with LaundryRooms LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) + LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) + LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) def test_db_populate(self): @@ -43,10 +63,10 @@ def test_db_populate(self): self.assertEqual("Captured snapshots!\n", out.getvalue()) # asserts that all rooms have been snapshotted - self.assertEqual(LaundrySnapshot.objects.all().count(), 4) + self.assertEqual(LaundrySnapshot.objects.all().count(), 3) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class TestLaundryRoomMigration(TestCase): def test_db_populate(self): out = StringIO() @@ -55,25 +75,63 @@ def test_db_populate(self): # tests the value of the output self.assertEqual("Uploaded Laundry Rooms!\n", out.getvalue()) - # asserts that the number of LaundryRooms created was 53 - self.assertEqual(LaundryRoom.objects.all().count(), 53) - + # compute expected number of rooms with open("laundry/data/laundry_data.csv") as data: reader = csv.reader(data) + # subtract 1 to account for header + num_rooms = sum(1 for _ in reader) - 1 - for i, row in enumerate(reader): - hall_id, hall_name, location, uuid, total_washers, total_dryers = row + # asserts that the number of LaundryRooms created was 53 + self.assertEqual(LaundryRoom.objects.all().count(), num_rooms) - room = LaundryRoom.objects.get(hall_id=hall_id) + with open("laundry/data/laundry_data.csv") as data: + reader = csv.DictReader(data) - # asserts that all fields of LaundryRoom are same - self.assertEqual(room.name, hall_name) - self.assertEqual(room.location, location) - self.assertEqual(str(room.uuid), uuid) - self.assertEqual(room.total_washers, int(total_washers)) - self.assertEqual(room.total_dryers, int(total_dryers)) + for row in reader: + room = LaundryRoom.objects.get(room_id=int(row["room_id"])) + # asserts that all fields of LaundryRoom are same + self.assertEqual(room.name, row["room_name"]) + self.assertEqual(room.location, row["room_description"]) + self.assertEqual(room.location_id, int(row["room_location"])) + self.assertEqual(room.total_washers, int(row["count_washers"])) + self.assertEqual(room.total_dryers, int(row["count_dryers"])) call_command("load_laundry_rooms") # asserts that LaundryRooms do not recreate itself - self.assertEqual(LaundryRoom.objects.all().count(), 53) + self.assertEqual(LaundryRoom.objects.all().count(), num_rooms) + + +@mock.patch("laundry.management.commands.update_laundry_rooms.get_validated", mock_laundry_get) +class TestUpdateLaundryRooms(TestCase): + @mock.patch("laundry.management.commands.update_laundry_rooms.write_file") + def test_update_laundry_rooms(self, mock_laundry_write): + call_command("update_laundry_rooms") + expected = [ + { + "room_id": 14089, + "room_name": "English House", + "room_description": "English House", + "room_location": 14146, + "count_washers": 3, + "count_dryers": 3, + }, + { + "room_id": 14099, + "room_name": "Harnwell 10th Floor", + "room_description": "Harnwell College House", + "room_location": 14150, + "count_washers": 3, + "count_dryers": 3, + }, + { + "room_id": 14100, + "room_name": "Harnwell 12th Floor", + "room_description": "Harnwell College House", + "room_location": 14150, + "count_washers": 3, + "count_dryers": 3, + }, + ] + # assert that the mock was called with this + mock_laundry_write.assert_called_with(expected) diff --git a/backend/tests/laundry/test_models.py b/backend/tests/laundry/test_models.py index 5dbd8fbc..3c8e2878 100644 --- a/backend/tests/laundry/test_models.py +++ b/backend/tests/laundry/test_models.py @@ -7,45 +7,53 @@ class LaundrySnapshotTestCase(TestCase): def setUp(self): # populates database with LaundryRooms LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 - ) - LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 - ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 - ) - self.laundry_room = LaundryRoom.objects.get(hall_id=0, name="Bishop White", location="Quad") + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, + ) + LaundryRoom.objects.get_or_create( + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, + ) + LaundryRoom.objects.get_or_create( + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, + ) + self.laundry_room = LaundryRoom.objects.get(room_id=14089) self.snapshot = LaundrySnapshot.objects.create( - room=self.laundry_room, available_washers=10, available_dryers=10 + room=self.laundry_room, available_washers=3, available_dryers=3 ) def test_str(self): self.assertEqual( str(self.snapshot), - f"Hall No. {self.snapshot.room.hall_id} | {self.snapshot.date.date()}", + f"Room {self.snapshot.room.name} | {self.snapshot.date.date()}", ) class LaundryRoomTestCase(TestCase): def setUp(self): # populates database with LaundryRooms - LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 - ) - LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 - ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + + self.room = LaundryRoom.objects.create( + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) - self.room = LaundryRoom.objects.create(hall_id=1, name="test hall", location="location") def test_str(self): - self.assertEqual(str(self.room), f"Hall No. {self.room.hall_id} | {self.room.name}") + self.assertEqual(str(self.room), f"Room {self.room.name} | {self.room.location}") diff --git a/backend/tests/laundry/test_views.py b/backend/tests/laundry/test_views.py index d0dae554..31ee5607 100644 --- a/backend/tests/laundry/test_views.py +++ b/backend/tests/laundry/test_views.py @@ -8,26 +8,38 @@ from rest_framework.test import APIClient from laundry.models import LaundryRoom, LaundrySnapshot -from tests.laundry.test_commands import fakeLaundryGet +from tests.laundry.test_commands import mock_laundry_get User = get_user_model() -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class HallIdViewTestCase(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 - ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) self.client = APIClient() @@ -37,31 +49,43 @@ def test_response(self): for hall in res_json: self.assertTrue( LaundryRoom.objects.filter( - name=hall["name"], hall_id=hall["hall_id"], location=hall["location"] + name=hall["name"], room_id=hall["hall_id"], location=hall["location"] ) ) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class HallInfoViewTestCase(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) - self.laundry_room = LaundryRoom.objects.get(hall_id=0, name="Bishop White", location="Quad") + self.laundry_room = LaundryRoom.objects.get(room_id=14089) self.client = APIClient() def test_response(self): - response = self.client.get(reverse("hall-info", args=[self.laundry_room.hall_id])) + response = self.client.get(reverse("hall-info", args=[self.laundry_room.room_id])) res_json = json.loads(response.content) if response.status_code == 200: self.assertEqual(self.laundry_room.name, res_json["hall_name"]) @@ -74,32 +98,44 @@ def test_hall_error(self): self.assertEqual(404, response.status_code) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class MultipleHallInfoViewTestCase(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 - ) - LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) - self.laundry_room = LaundryRoom.objects.get(hall_id=0, name="Bishop White", location="Quad") + self.laundry_room = LaundryRoom.objects.get(room_id=14089) self.client = APIClient() def test_response(self): - response = self.client.get(reverse("multiple-hall-info", args=["0,1,2,3"])) + response = self.client.get(reverse("multiple-hall-info", args=["0,1,2"])) res_json = json.loads(response.content) if response.status_code == 200: rooms = res_json["rooms"] for room in rooms: - self.assertTrue(LaundryRoom.objects.filter(hall_id=room["id"])) + self.assertTrue(LaundryRoom.objects.filter(room_id=room["id"])) self.assertIn("machines", room) self.assertIn("washers", room["machines"]) self.assertIn("dryers", room["machines"]) @@ -116,29 +152,41 @@ def test_hall_error(self): self.assertEqual(404, response.status_code) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class HallUsageViewTestCase(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 - ) - self.laundry_room = LaundryRoom.objects.get(hall_id=0, name="Bishop White", location="Quad") + self.laundry_room = LaundryRoom.objects.get(room_id=14089) self.snapshot = LaundrySnapshot.objects.create( - room=self.laundry_room, available_washers=5, available_dryers=10 + room=self.laundry_room, available_washers=3, available_dryers=3 ) self.client = APIClient() def test_response(self): - response = self.client.get(reverse("hall-usage", args=[self.laundry_room.hall_id])) + response = self.client.get(reverse("hall-usage", args=[self.laundry_room.room_id])) res_json = json.loads(response.content) time = timezone.localtime() @@ -148,27 +196,37 @@ def test_response(self): self.assertEqual(self.snapshot.available_dryers, res_json["dryer_data"][str(hour)]) -@mock.patch("requests.get", fakeLaundryGet) +@mock.patch("laundry.api_wrapper.get_validated", mock_laundry_get) class PreferencesTestCase(TestCase): def setUp(self): LaundryRoom.objects.get_or_create( - hall_id=0, name="Bishop White", location="Quad", total_washers=9, total_dryers=9 + room_id=14089, + name="English House", + location="English House", + location_id=14146, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=1, name="Chestnut Butcher", location="Quad", total_washers=11, total_dryers=11 + room_id=14099, + name="Harnwell 10th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) LaundryRoom.objects.get_or_create( - hall_id=2, name="Class of 1928 Fisher", location="Quad", total_washers=8, total_dryers=8 - ) - LaundryRoom.objects.get_or_create( - hall_id=3, name="Craig", location="Quad", total_washers=3, total_dryers=3 + room_id=14100, + name="Harnwell 12th Floor", + location="Harnwell College House", + location_id=14150, + total_washers=3, + total_dryers=3, ) self.client = APIClient() self.test_user = User.objects.create_user("user", "user@a.com", "user") - self.laundry_room = LaundryRoom.objects.get(hall_id=0, name="Bishop White", location="Quad") - self.other_laundry_room = LaundryRoom.objects.get( - hall_id=1, name="Chestnut Butcher", location="Quad" - ) + self.laundry_room = LaundryRoom.objects.get(room_id=14089) + self.other_laundry_room = LaundryRoom.objects.get(room_id=14099) self.test_user.profile.laundry_preferences.add(self.laundry_room) def test_get(self): @@ -176,13 +234,14 @@ def test_get(self): response = self.client.get(reverse("preferences")) res_json = json.loads(response.content) - self.assertIn(self.laundry_room.hall_id, res_json["rooms"]) + self.assertIn(self.laundry_room.room_id, res_json["rooms"]) def test_post(self): self.client.force_authenticate(user=self.test_user) - self.client.post(reverse("preferences"), {"rooms": [self.other_laundry_room.hall_id]}) + payload = json.dumps({"rooms": [self.other_laundry_room.room_id]}) + self.client.post(reverse("preferences"), payload, content_type="application/json") response = self.client.get(reverse("preferences")) res_json = json.loads(response.content) - self.assertIn(self.other_laundry_room.hall_id, res_json["rooms"]) + self.assertIn(self.other_laundry_room.room_id, res_json["rooms"]) diff --git a/backend/tests/penndata/test_views.py b/backend/tests/penndata/test_views.py index fea551a7..525c5dbb 100644 --- a/backend/tests/penndata/test_views.py +++ b/backend/tests/penndata/test_views.py @@ -113,15 +113,15 @@ def test_first_response(self): self.assertIn(636, dining_info) self.assertEqual(res_json[2]["type"], "laundry") - self.assertEqual(res_json[2]["info"]["room_id"], 0) + self.assertIn(res_json[2]["info"]["room_id"], [14089, 14099, 14100]) self.test_user.profile.dining_preferences.add(Venue.objects.get(venue_id=747)) self.test_user.profile.dining_preferences.add(Venue.objects.get(venue_id=1733)) self.test_user.profile.dining_preferences.add(Venue.objects.get(venue_id=638)) - self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(hall_id=3)) - self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(hall_id=4)) - self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(hall_id=5)) + self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(room_id=14089)) + self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(room_id=14099)) + self.test_user.profile.laundry_preferences.add(LaundryRoom.objects.get(room_id=14100)) new_response = self.client.get(reverse("homepage")) new_res_json = json.loads(new_response.content)["cells"] @@ -132,7 +132,7 @@ def test_first_response(self): self.assertIn(1733, new_dining_info) self.assertIn(638, new_dining_info) - self.assertEqual(new_res_json[2]["info"]["room_id"], 3) + self.assertEqual(new_res_json[2]["info"]["room_id"], 14089) self.assertEqual(new_res_json[1]["type"], "news") diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py new file mode 100644 index 00000000..e69de29b