From 39a4e14644261a39bfb5317cdccccd5187153965 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Thu, 8 Aug 2024 05:08:17 -0700 Subject: [PATCH 1/5] Refactor laundry.py to use NamedTuple, add tests Refactor laundry.py to store API output using NamedTuples rather than plain Python dicts. This makes the output more user-friendly, less error-prone, and easier to type hint. This refactoring also modifies the API behavior as follows: 1. JSON objects that don't qualify as a known laundry machine type (washer, dryer, or combo) will now be skipped, whereas previously they would be marked as "unknown". These objects should be skipped because they may represent things that aren't laundry machines, such as card readers. 2. The time_remaining field for laundry machines can now be None if the laundry machine is unavailable (either out of service or offline). Add unit tests for laundry.py to increase test coverage to 100%. The new tests use mocked data for Towers laundry machines, the vast majority of which are combo machines rather than standalone washers or dryers. Fix a heretofore unknown bug that was caught during the aforementioned unit testing: "washNdry" was misspelled as "washNDry", which was causing the API to mark all combo machines as "unknown". This typo hadn't been caught because there had been no unit tests that included data for combo machines. --- pittapi/laundry.py | 101 +- tests/laundry_test.py | 81 +- ...n => laundry_mock_response_suth_east.json} | 0 .../samples/laundry_mock_response_towers.json | 1452 +++++++++++++++++ 4 files changed, 1580 insertions(+), 54 deletions(-) rename tests/samples/{laundry_mock_response.json => laundry_mock_response_suth_east.json} (100%) create mode 100644 tests/samples/laundry_mock_response_towers.json diff --git a/pittapi/laundry.py b/pittapi/laundry.py index bbbca6a..1e469b3 100644 --- a/pittapi/laundry.py +++ b/pittapi/laundry.py @@ -20,7 +20,7 @@ from __future__ import annotations import requests -from typing import Any +from typing import Any, NamedTuple BASE_URL = "https://www.laundryview.com/api/currentRoomData?school_desc_key=197&location={location}" @@ -37,7 +37,22 @@ } -def _get_laundry_info(building_name: str) -> Any: +class BuildingStatus(NamedTuple): + building: str + free_washers: int + total_washers: int + free_dryers: int + total_dryers: int + + +class LaundryMachine(NamedTuple): + id: str + status: str + type: str + time_left: int | None + + +def _get_laundry_info(building_name: str) -> dict[str, Any]: """Returns JSON object of laundry view webpage""" building_name = building_name.upper() url = BASE_URL.format(location=LOCATION_LOOKUP[building_name]) @@ -46,10 +61,9 @@ def _get_laundry_info(building_name: str) -> Any: return info -def get_status_simple(building_name: str) -> dict[str, str]: +def get_building_status(building_name: str) -> BuildingStatus: """ - :returns: a dictionary with free washers and dryers as well as total washers - and dryers for given building + :returns: a BuildingStatus object with free washers and dryers as well as total washers and dryers for given building :param: loc: Building name, case doesn't matter -> TOWERS @@ -61,39 +75,43 @@ def get_status_simple(building_name: str) -> dict[str, str]: -> SUTH_WEST """ laundry_info = _get_laundry_info(building_name) - freeWashers, freeDryers, totalWashers, totalDryers = 0, 0, 0, 0 + free_washers, free_dryers, total_washers, total_dryers = 0, 0, 0, 0 for obj in laundry_info["objects"]: if obj["type"] == "washFL": - totalWashers += 1 + total_washers += 1 if obj["status_toggle"] == 0: - freeWashers += 1 + free_washers += 1 elif obj["type"] == "dry": - totalDryers += 1 + total_dryers += 1 if obj["status_toggle"] == 0: - freeDryers += 1 + free_dryers += 1 # for towers, they have "combo" machines with this type, no individual washers and dryers | # one part of combo being in use marks the whole thing as in use, so we can only show if # both parts are free. + # + # New: Above statement is not true, as the JSON objects provide two separate statuses for combo machines, one for the + # washer and one for the dryer + # See tests/samples/laundry_mock_response_towers.json for examples + # TODO: rewrite elif to use the separate combo statuses to more accurately count washers and dryers elif obj["type"] == "washNdry": - totalWashers += 1 - totalDryers += 1 + total_washers += 1 + total_dryers += 1 if obj["status_toggle"] == 0: - freeDryers += 1 - freeWashers += 1 - return { - "building": building_name, - "free_washers": freeWashers, - "total_washers": totalWashers, - "free_dryers": freeDryers, - "total_dryers": totalDryers, - } - - -def get_status_detailed(building_name: str) -> list[dict[str, str | int]]: + free_dryers += 1 + free_washers += 1 + return BuildingStatus( + building=building_name, + free_washers=free_washers, + total_washers=total_washers, + free_dryers=free_dryers, + total_dryers=total_dryers, + ) + + +def get_laundry_machine_statuses(building_name: str) -> list[LaundryMachine]: """ - :returns: A list of washers and dryers for the passed - building location with their statuses + :returns: A list of washers and dryers for the passed building location with their statuses :param building_name: (String) one of these: -> BRACKENRIDGE @@ -104,7 +122,6 @@ def get_status_detailed(building_name: str) -> list[dict[str, str | int]]: -> SUTH_WEST """ machines = [] - machine_type = "Unknown" laundry_info = _get_laundry_info(building_name) for obj in laundry_info["objects"]: @@ -112,20 +129,32 @@ def get_status_detailed(building_name: str) -> list[dict[str, str | int]]: machine_type = "dryer" elif obj["type"] == "washFL": machine_type = "washer" - elif obj["type"] == "washNDry": - machine_type = "washAndDry" + # TODO: rewrite elif to add two LaundryMachines for combo machines, one for the washer and one for the dryer + # Must first figure out which JSON fields correspond to the washer and which correspond to the dryer + elif obj["type"] == "washNdry": + machine_type = "combo" + # Skip any JSON object that's not a laundry machine (e.g., card reader) + else: + continue machine_id = obj["appliance_desc_key"] + # Possible machine statuses: + # status_toggle = 0: "Available" + # status_toggle = 1: TODO, unknown and must be documented, probably "Completed" + # status_toggle = 2: either "N min remaining" or "Ext. Cycle" + # status_toggle = 3: "Out of service" + # status_toggle = 4: "Offline" machine_status = obj["time_left_lite"] - time_left = obj["time_remaining"] + unavailable = machine_status in ("Out of service", "Offline") + time_left = None if unavailable else obj["time_remaining"] machines.append( - { - "machine_id": machine_id, - "machine_status": machine_status, - "machine_type": machine_type, - "time_left": time_left, - } + LaundryMachine( + id=machine_id, + status=machine_status, + type=machine_type, + time_left=time_left, + ) ) return machines diff --git a/tests/laundry_test.py b/tests/laundry_test.py index e708c75..0f01bf2 100644 --- a/tests/laundry_test.py +++ b/tests/laundry_test.py @@ -24,40 +24,85 @@ from pathlib import Path from pittapi import laundry +from pittapi.laundry import BuildingStatus SAMPLE_PATH = Path() / "tests" / "samples" -TEST_BUILDING = "SUTH_EAST" class LaundryTest(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) - with open(SAMPLE_PATH / "laundry_mock_response.json", "r") as file: - self.mock_data = json.load(file) + with open(SAMPLE_PATH / "laundry_mock_response_suth_east.json", "r") as file: + self.mock_data_suth_east = json.load(file) + with open(SAMPLE_PATH / "laundry_mock_response_towers.json", "r") as file: + self.mock_data_towers = json.load(file) @responses.activate - def test_get_status_simple(self): + def test_get_building_status_suth_east(self): + test_building = "SUTH_EAST" responses.add( responses.GET, - laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[TEST_BUILDING]), - json=self.mock_data, + laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), + json=self.mock_data_suth_east, status=200, ) - status = laundry.get_status_simple(TEST_BUILDING) - self.assertIsInstance(status, dict) - self.assertEqual(status["building"], TEST_BUILDING) - self.assertEqual(status["free_washers"], 7) - self.assertEqual(status["free_dryers"], 2) - self.assertEqual(status["total_washers"], 10) - self.assertEqual(status["total_dryers"], 10) + status = laundry.get_building_status(test_building) + self.assertEqual( + status, + BuildingStatus(building=test_building, free_washers=7, free_dryers=2, total_washers=10, total_dryers=10), + ) + + @responses.activate + def test_get_laundry_machine_statuses_suth_east(self): + test_building = "SUTH_EAST" + responses.add( + responses.GET, + laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), + json=self.mock_data_suth_east, + status=200, + ) + machines = laundry.get_laundry_machine_statuses(test_building) + self.assertIsInstance(machines, list) + self.assertEqual(len(machines), 20) + for machine in machines: + if machine.status in ("Available", "Ext. Cycle") or "remaining" in machine.status: + self.assertIsNotNone(machine.time_left) + elif machine.status in ("Out of service", "Offline"): + self.assertIsNone(machine.time_left) + else: + self.fail(f"Invalid machine status detected for {machine=}") + + @responses.activate + def test_get_building_status_towers(self): + test_building = "TOWERS" + responses.add( + responses.GET, + laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), + json=self.mock_data_towers, + status=200, + ) + status = laundry.get_building_status(test_building) + self.assertEqual( + status, + BuildingStatus(building=test_building, free_washers=1, free_dryers=1, total_washers=54, total_dryers=55), + ) @responses.activate - def test_get_status_detailed(self): + def test_get_laundry_machine_statuses_towers(self): + test_building = "TOWERS" responses.add( responses.GET, - laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[TEST_BUILDING]), - json=self.mock_data, + laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), + json=self.mock_data_towers, status=200, ) - status = laundry.get_status_detailed(TEST_BUILDING) - self.assertIsInstance(status, list) + machines = laundry.get_laundry_machine_statuses(test_building) + self.assertIsInstance(machines, list) + self.assertEqual(len(machines), 56) + for machine in machines: + if machine.status in ("Available", "Ext. Cycle") or "remaining" in machine.status: + self.assertIsNotNone(machine.time_left) + elif machine.status in ("Out of service", "Offline"): + self.assertIsNone(machine.time_left) + else: + self.fail(f"Invalid machine status detected for {machine=}") diff --git a/tests/samples/laundry_mock_response.json b/tests/samples/laundry_mock_response_suth_east.json similarity index 100% rename from tests/samples/laundry_mock_response.json rename to tests/samples/laundry_mock_response_suth_east.json diff --git a/tests/samples/laundry_mock_response_towers.json b/tests/samples/laundry_mock_response_towers.json new file mode 100644 index 0000000..7b762d0 --- /dev/null +++ b/tests/samples/laundry_mock_response_towers.json @@ -0,0 +1,1452 @@ +{ + "dimensions": { + "x": 394, + "y": 540 + }, + "roomOrientation": "3", + "machineScale": 63.73, + "upsideDownStacks": 0, + "objects": [ + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 173, + "y": 35, + "orientation": "NW", + "appliance_desc_key": "10000010828", + "appliance_desc": "GA03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "appliance_desc_key2": "10000010829", + "appliance_desc2": "GA04" + }, + { + "x": -80, + "y": 270, + "wall_1_x": 230, + "wall_1_y": 300, + "wall_2_x": -230, + "wall_2_y": 300, + "type": "D" + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SDGNYD", + "x": 124, + "y": 244, + "orientation": "NE", + "appliance_desc_key": "10000012757", + "appliance_desc": "GA17", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SDGNYD", + "x": 93, + "y": 244, + "orientation": "NE", + "appliance_desc_key": "10000012758", + "appliance_desc": "GA18", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0 + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175, + "y": 240, + "orientation": "SE", + "appliance_desc_key": "10000010772", + "appliance_desc": "BA01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010773", + "appliance_desc2": "BA02" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175, + "y": 215, + "orientation": "SE", + "appliance_desc_key": "10000010774", + "appliance_desc": "BA03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010775", + "appliance_desc2": "BA04" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175, + "y": 190, + "orientation": "SE", + "appliance_desc_key": "10000010776", + "appliance_desc": "BA05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010777", + "appliance_desc2": "BA06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 166, + "orientation": "SE", + "appliance_desc_key": "10000010778", + "appliance_desc": "BA07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 3, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Out of service", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010779", + "appliance_desc2": "BA08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 141, + "orientation": "SE", + "appliance_desc_key": "10000010780", + "appliance_desc": "BA09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010781", + "appliance_desc2": "BA10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 116, + "orientation": "SE", + "appliance_desc_key": "10000010782", + "appliance_desc": "BA11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 3, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Out of service", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010783", + "appliance_desc2": "BA12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 90, + "orientation": "SE", + "appliance_desc_key": "10000010784", + "appliance_desc": "BA13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010785", + "appliance_desc2": "BA14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 64, + "orientation": "SE", + "appliance_desc_key": "10000010786", + "appliance_desc": "BA15", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010787", + "appliance_desc2": "BA16" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 38, + "orientation": "SE", + "appliance_desc_key": "10000010788", + "appliance_desc": "BA17", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010789", + "appliance_desc2": "BA18" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -174, + "y": 14, + "orientation": "SE", + "appliance_desc_key": "10000010790", + "appliance_desc": "BA19", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010791", + "appliance_desc2": "BA20" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -89, + "orientation": "SE", + "appliance_desc_key": "10000010792", + "appliance_desc": "BB01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010793", + "appliance_desc2": "BB02" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -114, + "orientation": "SE", + "appliance_desc_key": "10000010794", + "appliance_desc": "BB03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010795", + "appliance_desc2": "BB04" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -139, + "orientation": "SE", + "appliance_desc_key": "10000010796", + "appliance_desc": "BB05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 3, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Out of service", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010797", + "appliance_desc2": "BB06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -163.01, + "orientation": "SE", + "appliance_desc_key": "10000010798", + "appliance_desc": "BB07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010799", + "appliance_desc2": "BB08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -187, + "orientation": "SE", + "appliance_desc_key": "10000010800", + "appliance_desc": "BB09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010801", + "appliance_desc2": "BB10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -175.49, + "y": -211.01, + "orientation": "SE", + "appliance_desc_key": "10000010802", + "appliance_desc": "BB11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010803", + "appliance_desc2": "BB12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -176, + "y": -236, + "orientation": "SE", + "appliance_desc_key": "10000010804", + "appliance_desc": "BB13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010805", + "appliance_desc2": "BB14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -236, + "orientation": "NW", + "appliance_desc_key": "10000010806", + "appliance_desc": "BC01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010807", + "appliance_desc2": "BC02" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -213, + "orientation": "NW", + "appliance_desc_key": "10000010808", + "appliance_desc": "BC03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010809", + "appliance_desc2": "BC04" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -189, + "orientation": "NW", + "appliance_desc_key": "10000010810", + "appliance_desc": "BC05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010811", + "appliance_desc2": "BC06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -166, + "orientation": "NW", + "appliance_desc_key": "10000010812", + "appliance_desc": "BC07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010813", + "appliance_desc2": "BC08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -143, + "orientation": "NW", + "appliance_desc_key": "10000010814", + "appliance_desc": "BC09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010815", + "appliance_desc2": "BC10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -119, + "orientation": "NW", + "appliance_desc_key": "10000010816", + "appliance_desc": "BC11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010817", + "appliance_desc2": "BC12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -95, + "orientation": "NW", + "appliance_desc_key": "10000010818", + "appliance_desc": "BC13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010819", + "appliance_desc2": "BC14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -71, + "orientation": "NW", + "appliance_desc_key": "10000010820", + "appliance_desc": "BC15", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010821", + "appliance_desc2": "BC16" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -47, + "orientation": "NW", + "appliance_desc_key": "10000010822", + "appliance_desc": "BC17", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010823", + "appliance_desc2": "BC18" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": -17, + "y": -23, + "orientation": "NW", + "appliance_desc_key": "10000010824", + "appliance_desc": "BC19", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010825", + "appliance_desc2": "BC20" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 173, + "y": 11, + "orientation": "NW", + "appliance_desc_key": "10000010826", + "appliance_desc": "GA01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010827", + "appliance_desc2": "GA02" + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "FRTWAS", + "x": 63, + "y": 244, + "orientation": "NE", + "appliance_desc_key": "10000012759", + "appliance_desc": "GA19", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 38, + "time_left_lite": "Out of service", + "percentage": 0 + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 173, + "y": 58, + "orientation": "NW", + "appliance_desc_key": "10000010830", + "appliance_desc": "GA05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010831", + "appliance_desc2": "GA06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 173, + "y": 82, + "orientation": "NW", + "appliance_desc_key": "10000010832", + "appliance_desc": "GA07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010833", + "appliance_desc2": "GA08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": 106, + "orientation": "NW", + "appliance_desc_key": "10000010834", + "appliance_desc": "GA09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010835", + "appliance_desc2": "GA10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": 129, + "orientation": "NW", + "appliance_desc_key": "10000010836", + "appliance_desc": "GA11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010837", + "appliance_desc2": "GA12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": 151, + "orientation": "NW", + "appliance_desc_key": "10000010838", + "appliance_desc": "GA13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 3, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Out of service", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010839", + "appliance_desc2": "GA14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": 175, + "orientation": "NW", + "appliance_desc_key": "10000010840", + "appliance_desc": "GA15", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010841", + "appliance_desc2": "GA16" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -236, + "orientation": "NW", + "appliance_desc_key": "10000010842", + "appliance_desc": "GB01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010843", + "appliance_desc2": "GB02" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -213, + "orientation": "NW", + "appliance_desc_key": "10000010844", + "appliance_desc": "GB03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010845", + "appliance_desc2": "GB04" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -189, + "orientation": "NW", + "appliance_desc_key": "10000010846", + "appliance_desc": "GB05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010847", + "appliance_desc2": "GB06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -165, + "orientation": "NW", + "appliance_desc_key": "10000010848", + "appliance_desc": "GB07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010849", + "appliance_desc2": "GB08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -141, + "orientation": "NW", + "appliance_desc_key": "10000010850", + "appliance_desc": "GB09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010851", + "appliance_desc2": "GB10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -118, + "orientation": "NW", + "appliance_desc_key": "10000010852", + "appliance_desc": "GB11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010853", + "appliance_desc2": "GB12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -95, + "orientation": "NW", + "appliance_desc_key": "10000010854", + "appliance_desc": "GB13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010855", + "appliance_desc2": "GB14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 172, + "y": -73, + "orientation": "NW", + "appliance_desc_key": "10000010856", + "appliance_desc": "GB15", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010857", + "appliance_desc2": "GB16" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -19, + "orientation": "SE", + "appliance_desc_key": "10000010858", + "appliance_desc": "GC01", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010859", + "appliance_desc2": "GC02" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -42, + "orientation": "SE", + "appliance_desc_key": "10000010860", + "appliance_desc": "GC03", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010861", + "appliance_desc2": "GC04" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -65, + "orientation": "SE", + "appliance_desc_key": "10000010862", + "appliance_desc": "GC05", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010863", + "appliance_desc2": "GC06" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -90, + "orientation": "SE", + "appliance_desc_key": "10000010864", + "appliance_desc": "GC07", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010865", + "appliance_desc2": "GC08" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -114, + "orientation": "SE", + "appliance_desc_key": "10000010866", + "appliance_desc": "GC09", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010867", + "appliance_desc2": "GC10" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -139, + "orientation": "SE", + "appliance_desc_key": "10000010868", + "appliance_desc": "GC11", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010869", + "appliance_desc2": "GC12" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -162, + "orientation": "SE", + "appliance_desc_key": "10000010870", + "appliance_desc": "GC13", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010871", + "appliance_desc2": "GC14" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -186, + "orientation": "SE", + "appliance_desc_key": "10000010872", + "appliance_desc": "GC15", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010873", + "appliance_desc2": "GC16" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -211, + "orientation": "SE", + "appliance_desc_key": "10000010874", + "appliance_desc": "GC17", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 4, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Offline", + "percentage2": 0, + "status_toggle": 4, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Offline", + "percentage": 0, + "appliance_desc_key2": "10000010875", + "appliance_desc2": "GC18" + }, + { + "type": "washNdry", + "appliance_type": "D", + "model_number": "COMBO", + "x": 25, + "y": -236, + "orientation": "SE", + "appliance_desc_key": "10000010876", + "appliance_desc": "GC19", + "combo": true, + "stacked": false, + "opacity": 0, + "status_toggle2": 3, + "average_run_time2": 60, + "time_remaining2": 38, + "time_left_lite2": "Out of service", + "percentage2": 0, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Out of service", + "percentage": 0, + "appliance_desc_key2": "10000010877", + "appliance_desc2": "GC20" + }, + { + "type": "tableLg", + "x": 174, + "y": 226.58, + "orientation": "NW" + }, + { + "type": "cardReader", + "x": -17, + "y": 20, + "orientation": "NW" + }, + { + "type": "cardReader", + "x": 25, + "y": 22, + "orientation": "SE" + }, + { + "type": "cardReader", + "x": -18, + "y": 38, + "orientation": "NW" + }, + { + "type": "cardReader", + "x": -17, + "y": 60, + "orientation": "NW" + }, + { + "type": "cardReader", + "x": 25, + "y": 39, + "orientation": "SE" + }, + { + "type": "cardReader", + "x": 25, + "y": 59, + "orientation": "SE" + } + ], + "schoolStyles": { + "school_theme": "default", + "school_logo": "5e6ae1ae-1eef-457a-bcbe-2a069a2332d4", + "room_wall": "#B58500", + "room_bg": "#172656", + "header_status_legend_bg": "#003594", + "header_status_legend_color": "#B58500", + "content_box_bg": "#CACACA", + "content_box_color": "#003594" + }, + "userNotifications": {} +} From f43bb5c98f3bbfc32edbe696530a22e544f1610e Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Fri, 9 Aug 2024 23:41:46 -0700 Subject: [PATCH 2/5] Fix indentation in laundry_test.py __init__ --- tests/laundry_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/laundry_test.py b/tests/laundry_test.py index 0f01bf2..e2b4881 100644 --- a/tests/laundry_test.py +++ b/tests/laundry_test.py @@ -34,8 +34,8 @@ def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) with open(SAMPLE_PATH / "laundry_mock_response_suth_east.json", "r") as file: self.mock_data_suth_east = json.load(file) - with open(SAMPLE_PATH / "laundry_mock_response_towers.json", "r") as file: - self.mock_data_towers = json.load(file) + with open(SAMPLE_PATH / "laundry_mock_response_towers.json", "r") as file: + self.mock_data_towers = json.load(file) @responses.activate def test_get_building_status_suth_east(self): From a686ecc221b872ea3f4104ef643e76052c565568 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 10 Aug 2024 20:28:24 -0700 Subject: [PATCH 3/5] Remove outdated comment --- pittapi/laundry.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pittapi/laundry.py b/pittapi/laundry.py index 1e469b3..09f8239 100644 --- a/pittapi/laundry.py +++ b/pittapi/laundry.py @@ -86,14 +86,12 @@ def get_building_status(building_name: str) -> BuildingStatus: total_dryers += 1 if obj["status_toggle"] == 0: free_dryers += 1 - # for towers, they have "combo" machines with this type, no individual washers and dryers | - # one part of combo being in use marks the whole thing as in use, so we can only show if - # both parts are free. - # - # New: Above statement is not true, as the JSON objects provide two separate statuses for combo machines, one for the - # washer and one for the dryer + # The JSON objects provide two separate statuses for combo machines, one for the washer and one for the dryer # See tests/samples/laundry_mock_response_towers.json for examples + # + # This elif marks a whole combo machine as free if one part is free # TODO: rewrite elif to use the separate combo statuses to more accurately count washers and dryers + # Must first figure out which JSON fields correspond to the washer and which correspond to the dryer elif obj["type"] == "washNdry": total_washers += 1 total_dryers += 1 From 3140f4ce2027564a018bed3e03c4e10a252a7ff9 Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sun, 11 Aug 2024 15:57:18 -0700 Subject: [PATCH 4/5] Add type hint to fix mypy --strict error --- pittapi/laundry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pittapi/laundry.py b/pittapi/laundry.py index 09f8239..05e0495 100644 --- a/pittapi/laundry.py +++ b/pittapi/laundry.py @@ -57,7 +57,7 @@ def _get_laundry_info(building_name: str) -> dict[str, Any]: building_name = building_name.upper() url = BASE_URL.format(location=LOCATION_LOOKUP[building_name]) response = requests.get(url) - info = response.json() + info: dict[str, Any] = response.json() return info From 8847a4c61349cf6ac5d7e9c97c8a2d612a82756c Mon Sep 17 00:00:00 2001 From: Tianyi Zheng Date: Sat, 24 Aug 2024 13:40:14 -0700 Subject: [PATCH 5/5] Complete TODOs for combo machines and status_toggle = 1 Replace mock JSON test data for Sutherland East with new mock JSON test data for Holland. Because this data was collected close to the start of the fall semester, when the laundry machines are actually being used, it contains instances of status_toggle = 1, which clears up the TODO of what that status value represented. The meaning of this value has now been documented. Rewrite the logic for parsing JSON objects for laundry machines. The logic now accurately parses combo machines and adds support for double machines, which are two washers or two dryers in a single machine. This resolves the two remaining TODOs about combo machines. --- pittapi/laundry.py | 142 ++-- tests/laundry_test.py | 28 +- .../laundry_mock_response_holland.json | 643 ++++++++++++++++++ .../laundry_mock_response_suth_east.json | 382 ----------- 4 files changed, 747 insertions(+), 448 deletions(-) create mode 100644 tests/samples/laundry_mock_response_holland.json delete mode 100644 tests/samples/laundry_mock_response_suth_east.json diff --git a/pittapi/laundry.py b/pittapi/laundry.py index 05e0495..42fd0ea 100644 --- a/pittapi/laundry.py +++ b/pittapi/laundry.py @@ -19,9 +19,12 @@ from __future__ import annotations +import re import requests from typing import Any, NamedTuple +JSON = dict[str, Any] + BASE_URL = "https://www.laundryview.com/api/currentRoomData?school_desc_key=197&location={location}" @@ -36,6 +39,8 @@ "FORBES_CRAIG": "2430142", } +NUMBER_REGEX = re.compile(r"\d+") + class BuildingStatus(NamedTuple): building: str @@ -46,13 +51,14 @@ class BuildingStatus(NamedTuple): class LaundryMachine(NamedTuple): + name: str id: str status: str type: str time_left: int | None -def _get_laundry_info(building_name: str) -> dict[str, Any]: +def _get_laundry_info(building_name: str) -> JSON: """Returns JSON object of laundry view webpage""" building_name = building_name.upper() url = BASE_URL.format(location=LOCATION_LOOKUP[building_name]) @@ -61,6 +67,82 @@ def _get_laundry_info(building_name: str) -> dict[str, Any]: return info +def _parse_laundry_object_json(json: JSON) -> list[LaundryMachine]: + """ + Parse the given JSON object into a list of laundry machines. + Returns a list because a single machine may have multiple components + (one washer and one dryer, two washers, or two dryers). + + Implementation detail: the machine type is determined by checking the "type" JSON field. + While it'd be more straightforward to check the "combo" boolean field, this field won't exist if the JSON object doesn't + represent a laundry machine (e.g., a card reader). + + Possible machine statuses: + status_toggle = 0: "Available" + status_toggle = 1: "Idle" (finished running) + status_toggle = 2: either "N min remaining" or "Ext. Cycle" (currently running) + status_toggle = 3: "Out of service" + status_toggle = 4: "Offline" + """ + if json["type"] == "washNdry": # Combo machine, add washer and dryer separately + # Only Towers and Lothrop have combo machines, and for those buildings, + # washers are named with even numbers while dryers are named with odd numbers + machine1_name = json["appliance_desc"] + machine1_num_match = NUMBER_REGEX.search(machine1_name) + if not machine1_num_match: + raise ValueError(f"Found a combo machine with an invalid machine name: {machine1_name}") + machine1_num = int(machine1_num_match.group(0)) + machine1_type = "washer" if machine1_num % 2 == 0 else "dryer" + machine1_id = json["appliance_desc_key"] + machine1_status = json["time_left_lite"] + unavailable1 = machine1_status in ("Out of service", "Offline") + time_left1 = None if unavailable1 else json["time_remaining"] + + machine2_name = json["appliance_desc2"] + machine2_num_match = NUMBER_REGEX.search(machine2_name) + if not machine2_num_match: + raise ValueError(f"Found a combo machine with an invalid machine name: {machine2_name}") + machine2_num = int(machine2_num_match.group(0)) + machine2_type = "washer" if machine2_num % 2 == 0 else "dryer" + machine2_id = json["appliance_desc_key2"] + machine2_status = json["time_left_lite2"] + unavailable2 = machine2_status in ("Out of service", "Offline") + time_left2 = None if unavailable2 else json["time_remaining2"] + + return [ + LaundryMachine( + name=machine1_name, id=machine1_id, status=machine1_status, type=machine1_type, time_left=time_left1 + ), + LaundryMachine( + name=machine2_name, id=machine2_id, status=machine2_status, type=machine2_type, time_left=time_left2 + ), + ] + elif json["type"] in ("washFL", "dry"): # Only washers/only dryers + machine_type = "washer" if json["type"] == "washFL" else "dryer" + machine_name = json["appliance_desc"] + machine_id = json["appliance_desc_key"] + machine_status = json["time_left_lite"] + unavailable = machine_status in ("Out of service", "Offline") + time_left = None if unavailable else json["time_remaining"] + machines = [ + LaundryMachine(name=machine_name, id=machine_id, status=machine_status, type=machine_type, time_left=time_left) + ] + + if "type2" in json: # Double machine (two washers/two dryers), add second component separately + machine_type = "washer" if json["type2"] == "washFL" else "dryer" + machine_name = json["appliance_desc2"] + machine_id = json["appliance_desc_key2"] + machine_status = json["time_left_lite2"] + unavailable = machine_status in ("Out of service", "Offline") + time_left = None if unavailable else json["time_remaining2"] + machines.append( + LaundryMachine(name=machine_name, id=machine_id, status=machine_status, type=machine_type, time_left=time_left) + ) + + return machines + return [] # Not a laundry machine (card reader, table, etc.) + + def get_building_status(building_name: str) -> BuildingStatus: """ :returns: a BuildingStatus object with free washers and dryers as well as total washers and dryers for given building @@ -74,30 +156,17 @@ def get_building_status(building_name: str) -> BuildingStatus: -> SUTH_EAST -> SUTH_WEST """ - laundry_info = _get_laundry_info(building_name) + machines = get_laundry_machine_statuses(building_name) free_washers, free_dryers, total_washers, total_dryers = 0, 0, 0, 0 - - for obj in laundry_info["objects"]: - if obj["type"] == "washFL": + for machine in machines: + if machine.type == "washer": total_washers += 1 - if obj["status_toggle"] == 0: + if machine.status == "Available": free_washers += 1 - elif obj["type"] == "dry": - total_dryers += 1 - if obj["status_toggle"] == 0: - free_dryers += 1 - # The JSON objects provide two separate statuses for combo machines, one for the washer and one for the dryer - # See tests/samples/laundry_mock_response_towers.json for examples - # - # This elif marks a whole combo machine as free if one part is free - # TODO: rewrite elif to use the separate combo statuses to more accurately count washers and dryers - # Must first figure out which JSON fields correspond to the washer and which correspond to the dryer - elif obj["type"] == "washNdry": - total_washers += 1 + elif machine.type == "dryer": total_dryers += 1 - if obj["status_toggle"] == 0: + if machine.status == "Available": free_dryers += 1 - free_washers += 1 return BuildingStatus( building=building_name, free_washers=free_washers, @@ -123,36 +192,7 @@ def get_laundry_machine_statuses(building_name: str) -> list[LaundryMachine]: laundry_info = _get_laundry_info(building_name) for obj in laundry_info["objects"]: - if obj["type"] == "dry": - machine_type = "dryer" - elif obj["type"] == "washFL": - machine_type = "washer" - # TODO: rewrite elif to add two LaundryMachines for combo machines, one for the washer and one for the dryer - # Must first figure out which JSON fields correspond to the washer and which correspond to the dryer - elif obj["type"] == "washNdry": - machine_type = "combo" - # Skip any JSON object that's not a laundry machine (e.g., card reader) - else: - continue - - machine_id = obj["appliance_desc_key"] - # Possible machine statuses: - # status_toggle = 0: "Available" - # status_toggle = 1: TODO, unknown and must be documented, probably "Completed" - # status_toggle = 2: either "N min remaining" or "Ext. Cycle" - # status_toggle = 3: "Out of service" - # status_toggle = 4: "Offline" - machine_status = obj["time_left_lite"] - unavailable = machine_status in ("Out of service", "Offline") - time_left = None if unavailable else obj["time_remaining"] - - machines.append( - LaundryMachine( - id=machine_id, - status=machine_status, - type=machine_type, - time_left=time_left, - ) - ) + obj_machines = _parse_laundry_object_json(obj) + machines.extend(obj_machines) return machines diff --git a/tests/laundry_test.py b/tests/laundry_test.py index e2b4881..4f25b02 100644 --- a/tests/laundry_test.py +++ b/tests/laundry_test.py @@ -32,40 +32,39 @@ class LaundryTest(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) - with open(SAMPLE_PATH / "laundry_mock_response_suth_east.json", "r") as file: - self.mock_data_suth_east = json.load(file) + with open(SAMPLE_PATH / "laundry_mock_response_holland.json", "r") as file: + self.mock_data_holland = json.load(file) with open(SAMPLE_PATH / "laundry_mock_response_towers.json", "r") as file: self.mock_data_towers = json.load(file) @responses.activate - def test_get_building_status_suth_east(self): - test_building = "SUTH_EAST" + def test_get_building_status_holland(self): + test_building = "HOLLAND" responses.add( responses.GET, laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), - json=self.mock_data_suth_east, + json=self.mock_data_holland, status=200, ) status = laundry.get_building_status(test_building) self.assertEqual( status, - BuildingStatus(building=test_building, free_washers=7, free_dryers=2, total_washers=10, total_dryers=10), + BuildingStatus(building=test_building, free_washers=0, free_dryers=15, total_washers=14, total_dryers=21), ) @responses.activate - def test_get_laundry_machine_statuses_suth_east(self): - test_building = "SUTH_EAST" + def test_get_laundry_machine_statuses_holland(self): + test_building = "HOLLAND" responses.add( responses.GET, laundry.BASE_URL.format(location=laundry.LOCATION_LOOKUP[test_building]), - json=self.mock_data_suth_east, + json=self.mock_data_holland, status=200, ) machines = laundry.get_laundry_machine_statuses(test_building) - self.assertIsInstance(machines, list) - self.assertEqual(len(machines), 20) + self.assertEqual(len(machines), 35) for machine in machines: - if machine.status in ("Available", "Ext. Cycle") or "remaining" in machine.status: + if machine.status in ("Available", "Idle", "Ext. Cycle") or "remaining" in machine.status: self.assertIsNotNone(machine.time_left) elif machine.status in ("Out of service", "Offline"): self.assertIsNone(machine.time_left) @@ -97,10 +96,9 @@ def test_get_laundry_machine_statuses_towers(self): status=200, ) machines = laundry.get_laundry_machine_statuses(test_building) - self.assertIsInstance(machines, list) - self.assertEqual(len(machines), 56) + self.assertEqual(len(machines), 109) for machine in machines: - if machine.status in ("Available", "Ext. Cycle") or "remaining" in machine.status: + if machine.status in ("Available", "Idle", "Ext. Cycle") or "remaining" in machine.status: self.assertIsNotNone(machine.time_left) elif machine.status in ("Out of service", "Offline"): self.assertIsNone(machine.time_left) diff --git a/tests/samples/laundry_mock_response_holland.json b/tests/samples/laundry_mock_response_holland.json new file mode 100644 index 0000000..ba90889 --- /dev/null +++ b/tests/samples/laundry_mock_response_holland.json @@ -0,0 +1,643 @@ +{ + "dimensions": { + "x": 460, + "y": 620 + }, + "roomOrientation": "1", + "machineScale": 111, + "upsideDownStacks": 0, + "objects": [ + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -254.65, + "orientation": "NW", + "appliance_desc_key": "47699", + "appliance_desc": "B 7", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 15, + "time_left_lite": "15 min remaining", + "percentage": 0.5161290322580645 + }, + { + "type": "cardReader", + "x": 222.05, + "y": 248.9, + "orientation": "NW" + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -170.7, + "orientation": "NW", + "appliance_desc_key": "47701", + "appliance_desc": "B 9", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 30, + "time_left_lite": "30 min remaining", + "percentage": 0 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -128.75, + "orientation": "NW", + "appliance_desc_key": "47702", + "appliance_desc": "B10", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Out of service", + "percentage": 158.47368421052633 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -86.8, + "orientation": "NW", + "appliance_desc_key": "47703", + "appliance_desc": "B11", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 2.111111111111111 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -44.85, + "orientation": "NW", + "appliance_desc_key": "47704", + "appliance_desc": "B12", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 45.333333333333336 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -2.85, + "orientation": "NW", + "appliance_desc_key": "47705", + "appliance_desc": "B13", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 2.0481927710843375 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": -208.05, + "y": 70.55, + "orientation": "SE", + "appliance_desc_key": "47706", + "appliance_desc": "G 7", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 6.4375 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "FRTWAS", + "x": -208.05, + "y": 28.6, + "orientation": "SE", + "appliance_desc_key": "47707", + "appliance_desc": "G 8", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 39.41025641025641 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "FRTWAS", + "x": -208.05, + "y": -13.35, + "orientation": "SE", + "appliance_desc_key": "47708", + "appliance_desc": "G 9", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 51.51724137931034 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": -208.05, + "y": -55.3, + "orientation": "SE", + "appliance_desc_key": "47709", + "appliance_desc": "G10", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 46.65625 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": -208.05, + "y": -97.3, + "orientation": "SE", + "appliance_desc_key": "47710", + "appliance_desc": "G11", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 29, + "time_left_lite": "29 min remaining", + "percentage": 0.14705882352941177 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": -208.05, + "y": -139.25, + "orientation": "SE", + "appliance_desc_key": "47711", + "appliance_desc": "G12", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 3, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Out of service", + "percentage": 3436.7419354838707 + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": -208.05, + "y": -181.2, + "orientation": "SE", + "appliance_desc_key": "47712", + "appliance_desc": "G13", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 41.13333333333333 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 238.4, + "orientation": "SE", + "appliance_desc_key": "47714", + "appliance_desc": "B 2", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "washFL", + "appliance_type": "W", + "model_number": "SWNNYW", + "x": 211.55, + "y": -212.65, + "orientation": "NW", + "appliance_desc_key": "47700", + "appliance_desc": "B 8", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 6.0344827586206895 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 196.45, + "orientation": "SE", + "appliance_desc_key": "47716", + "appliance_desc": "B 4", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "cardReader", + "x": 222.05, + "y": 206.95, + "orientation": "NW" + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 154.5, + "orientation": "SE", + "appliance_desc_key": "47718", + "appliance_desc": "B 6", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -82.2, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47726", + "appliance_desc": "G 6", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": 211.55, + "y": 60.05, + "orientation": "NW", + "appliance_desc_key": "47720", + "appliance_desc": "B15", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 18, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -124.15, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47724", + "appliance_desc": "G 4", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "redundant": true + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -166.1, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47722", + "appliance_desc": "G 2", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 4, + "time_left_lite": "4 min remaining", + "percentage": 0.9333333333333333, + "redundant": true + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -166.1, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47721", + "appliance_desc": "G 1", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 2, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -166.1, + "y2": -286.1, + "appliance_desc_key2": "47722", + "appliance_desc2": "G 2", + "opacity2": 1, + "status_toggle2": 2, + "average_run_time2": 60, + "time_remaining2": 4, + "time_left_lite2": "4 min remaining", + "percentage2": 0.9333333333333333 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": 211.55, + "y": 60.05, + "orientation": "NW", + "appliance_desc_key": "47719", + "appliance_desc": "B14", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 18, + "time_left_lite": "Available", + "percentage": 0, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": 211.55, + "y2": 60.05, + "appliance_desc_key2": "47720", + "appliance_desc2": "B15", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 18, + "time_left_lite2": "Available", + "percentage2": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -124.15, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47723", + "appliance_desc": "G 3", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 54, + "time_left_lite": "54 min remaining", + "percentage": 0.1, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -124.15, + "y2": -286.1, + "appliance_desc_key2": "47724", + "appliance_desc2": "G 4", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 154.5, + "orientation": "SE", + "appliance_desc_key": "47717", + "appliance_desc": "B 5", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -208.05, + "y2": 154.5, + "appliance_desc_key2": "47718", + "appliance_desc2": "B 6", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -82.2, + "y": -286.1, + "orientation": "SW", + "appliance_desc_key": "47725", + "appliance_desc": "G 5", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 1, + "average_run_time": 60, + "time_remaining": 0, + "time_left_lite": "Idle", + "percentage": 3.6, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -82.2, + "y2": -286.1, + "appliance_desc_key2": "47726", + "appliance_desc2": "G 6", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 196.45, + "orientation": "SE", + "appliance_desc_key": "47715", + "appliance_desc": "B 3", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 0, + "average_run_time": 60, + "time_remaining": 60, + "time_left_lite": "Available", + "percentage": 0, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -208.05, + "y2": 196.45, + "appliance_desc_key2": "47716", + "appliance_desc2": "B 4", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0 + }, + { + "type": "dry", + "appliance_type": "D", + "model_number": "SSENYD", + "x": -208.05, + "y": 238.4, + "orientation": "SE", + "appliance_desc_key": "47713", + "appliance_desc": "B 1", + "combo": false, + "stacked": false, + "opacity": 1, + "status_toggle": 2, + "average_run_time": 60, + "time_remaining": 59, + "time_left_lite": "59 min remaining", + "percentage": 0.016666666666666666, + "manualStack": true, + "type2": "dry", + "appliance_type2": "D", + "model_number2": "SSENYD", + "x2": -208.05, + "y2": 238.4, + "appliance_desc_key2": "47714", + "appliance_desc2": "B 2", + "opacity2": 1, + "status_toggle2": 0, + "average_run_time2": 60, + "time_remaining2": 60, + "time_left_lite2": "Available", + "percentage2": 0 + } + ], + "schoolStyles": { + "school_theme": "default", + "school_logo": "5e6ae1ae-1eef-457a-bcbe-2a069a2332d4", + "room_wall": "#B58500", + "room_bg": "#172656", + "header_status_legend_bg": "#003594", + "header_status_legend_color": "#B58500", + "content_box_bg": "#CACACA", + "content_box_color": "#003594" + }, + "userNotifications": {} +} diff --git a/tests/samples/laundry_mock_response_suth_east.json b/tests/samples/laundry_mock_response_suth_east.json deleted file mode 100644 index e2e96e3..0000000 --- a/tests/samples/laundry_mock_response_suth_east.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "dimensions": { - "x": 320, - "y": 460 - }, - "roomOrientation": "1", - "machineScale": 100, - "upsideDownStacks": 0, - "objects": [ - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": -65, - "y": -195, - "orientation": "SW", - "appliance_desc_key": "47689", - "appliance_desc": "01", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 18, - "time_left_lite": "18 min remaining", - "percentage": 0.660377358490566 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "FRTWAS", - "x": 137, - "y": -188, - "orientation": "NW", - "appliance_desc_key": "47679", - "appliance_desc": "05", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 139, - "y": -108, - "orientation": "NW", - "appliance_desc_key": "47681", - "appliance_desc": "07", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 27, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 138, - "y": -68, - "orientation": "NW", - "appliance_desc_key": "47682", - "appliance_desc": "08", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 137, - "y": -28, - "orientation": "NW", - "appliance_desc_key": "47683", - "appliance_desc": "09", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 19, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 138, - "y": 12, - "orientation": "NW", - "appliance_desc_key": "47684", - "appliance_desc": "10", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 3, - "time_left_lite": "3 min remaining", - "percentage": 0.9 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 138, - "y": 52, - "orientation": "NW", - "appliance_desc_key": "47685", - "appliance_desc": "11", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 138, - "y": 92, - "orientation": "NW", - "appliance_desc_key": "47686", - "appliance_desc": "12", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 137, - "y": 130, - "orientation": "NW", - "appliance_desc_key": "47687", - "appliance_desc": "13", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 137, - "y": 170, - "orientation": "NW", - "appliance_desc_key": "47688", - "appliance_desc": "14", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 3, - "average_run_time": 60, - "time_remaining": 0, - "time_left_lite": "Out of service", - "percentage": 130.61363636363637 - }, - { - "type": "washFL", - "appliance_type": "W", - "model_number": "MAT12W", - "x": 137, - "y": -149, - "orientation": "NW", - "appliance_desc_key": "47680", - "appliance_desc": "06", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 36, - "time_left_lite": "36 min remaining", - "percentage": 0.4375 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": -25, - "y": -195, - "orientation": "SW", - "appliance_desc_key": "47690", - "appliance_desc": "02", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 0, - "time_left_lite": "Ext. Cycle", - "percentage": 9.520833333333334 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 14, - "y": -196, - "orientation": "SW", - "appliance_desc_key": "47691", - "appliance_desc": "03", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 33, - "time_left_lite": "33 min remaining", - "percentage": 0.32653061224489793 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 53, - "y": -196, - "orientation": "SW", - "appliance_desc_key": "47692", - "appliance_desc": "04", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 45, - "time_left_lite": "45 min remaining", - "percentage": 0.08163265306122448 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 46, - "y": 10, - "orientation": "SW", - "appliance_desc_key": "47693", - "appliance_desc": "15", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 57, - "time_left_lite": "Available", - "percentage": 0 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 6, - "y": 10, - "orientation": "SW", - "appliance_desc_key": "47694", - "appliance_desc": "16", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 48, - "time_left_lite": "48 min remaining", - "percentage": 0.15789473684210525 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": -34, - "y": 10, - "orientation": "SW", - "appliance_desc_key": "47695", - "appliance_desc": "17", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 0, - "time_left_lite": "Ext. Cycle", - "percentage": 1.105263157894737 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 45, - "y": -30, - "orientation": "NE", - "appliance_desc_key": "47696", - "appliance_desc": "18", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 51, - "time_left_lite": "51 min remaining", - "percentage": 0.019230769230769232 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": 5, - "y": -31, - "orientation": "NE", - "appliance_desc_key": "47697", - "appliance_desc": "19", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 2, - "average_run_time": 60, - "time_remaining": 0, - "time_left_lite": "Ext. Cycle", - "percentage": 1.1041666666666667 - }, - { - "type": "dry", - "appliance_type": "D", - "model_number": "MDG16D", - "x": -35, - "y": -31, - "orientation": "NE", - "appliance_desc_key": "47698", - "appliance_desc": "20", - "combo": false, - "stacked": false, - "opacity": 1, - "status_toggle": 0, - "average_run_time": 60, - "time_remaining": 60, - "time_left_lite": "Available", - "percentage": 0 - } - ], - "schoolStyles": { - "school_theme": "default", - "school_logo": "5e6ae1ae-1eef-457a-bcbe-2a069a2332d4", - "room_wall": "#B58500", - "room_bg": "#172656", - "header_status_legend_bg": "#003594", - "header_status_legend_color": "#B58500", - "content_box_bg": "#CACACA", - "content_box_color": "#003594" - }, - "userNotifications": {} -}