Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laundry update #105

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
Binary file added penn/.DS_Store
Binary file not shown.
106 changes: 53 additions & 53 deletions penn/data/laundry.csv
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
0,Bishop White,Quad,5faec7e9-a4aa-47c2-a514-950c03fac460
1,Chestnut Butcher,Quad,7dfa4b34-f44a-4a38-a6b9-44cdb968a915
2,Class of 1928 Fisher,Quad,e6697dca-d164-4980-8843-ea0a29b1cf49
3,Craig,Quad,37d661ce-3e50-4746-ab68-a5c61cd0bd0a
4,DuBois,DuBois,3ffa8978-e742-4076-9bcb-4a3e5c0eca92
5,English House,KCEH,b655a5be-1287-4ce2-b693-e9c1ae526f38
6,Harnwell Floor 02,Harnwell,1c7a9fb3-a938-4756-83c6-42d601d46036
7,Harnwell Floor 04,Harnwell,fba67cc0-336e-42f7-9603-c0b8a0e5030c
8,Harnwell Floor 06,Harnwell,87195ec7-eb3d-42fd-84aa-d63f4e45e285
9,Harnwell Floor 08,Harnwell,1bbb2ff6-d5e6-406d-a3a2-96c7972cceeb
10,Harnwell Floor 10,Harnwell,987bf30b-e8e1-4a9e-b842-c9cd8aeafddc
11,Harnwell Floor 12,Harnwell,dcb76f10-0137-4783-8604-bece4111b6dd
12,Harnwell Floor 14,Harnwell,941b2fcb-2b1b-4afd-8e8e-c100fbcbe0f2
13,Harnwell Floor 16,Harnwell,c74b2798-2d09-42a6-b65c-a5834219be59
14,Harnwell Floor 18,Harnwell,f30af904-72ad-49f6-aecf-f44c8301fb6b
15,Harnwell Floor 20,Harnwell,80a413fd-e0fa-456d-b922-f1576ded1f98
16,Harnwell Floor 22,Harnwell,35119e5e-92c0-45fb-bfeb-f2059196f644
17,Harnwell Floor 24,Harnwell,5880b051-8216-4cf4-92d6-5c7475f43eea
18,Harrison Floor 04,Harrison,447b5682-4c3c-441d-ab49-5f45aee6991f
19,Harrison Floor 06,Harrison,f77f7c68-f719-4843-8987-d64dabc0abff
20,Harrison Floor 08,Harrison,6561bb14-634f-437d-84fd-a0837ef991e7
21,Harrison Floor 10,Harrison,2dd7a63d-7d13-48e5-b038-98054b4f039f
22,Harrison Floor 12,Harrison,fdb607c7-63eb-4d55-a312-0c16b682cbe7
23,Harrison Floor 14,Harrison,53fdd440-e887-49e1-9ca9-7bb3cb4ab541
24,Harrison Floor 16,Harrison,8cedf60a-8f87-4128-89dd-4c75343ca64a
25,Harrison Floor 18,Harrison,116a8d6f-045b-47a5-b3f7-af31f4e661eb
26,Harrison Floor 20,Harrison,f6a8b303-1302-49e6-be53-c8e345316ed8
27,Harrison Floor 22,Harrison,b21c78af-1ebf-418c-a73b-85dc5ff49763
28,Harrison Floor 24,Harrison,9b95c471-053c-46ea-bc3b-d23bcad7a3a1
29,Hill House,Hill,82a00eb7-f70d-4a4c-9f0a-c2dafa4b67ea
30,Magee Amhurst,Quad,f6825dac-5a5a-4e4b-b66f-ea8226cbe78e
31,Mayer,Stouffer,6e3531d1-eebd-48b4-ad04-cf5983d42b02
32,Morgan,Quad,f249ca9f-ef84-4a35-9477-449b14612057
33,Rodin Floor 02,Rodin,7f25802d-31ad-4f80-ba26-d68a3f403aa8
34,Rodin Floor 04,Rodin,49e560fb-c1aa-4c98-a88a-cc9564481ec0
35,Rodin Floor 06,Rodin,701ce966-aa3c-4063-b3db-548ad89cb643
36,Rodin Floor 08,Rodin,4998a8a2-fb86-4900-bcb7-9d7cc6d9b938
37,Rodin Floor 10,Rodin,030c81c4-2300-4e8e-ae3a-303397a2e216
38,Rodin Floor 12,Rodin,c561f889-5898-41ba-99f5-2e6d4243e4d3
39,Rodin Floor 14,Rodin,2d211700-5b59-4c61-8922-991c0f7d7c15
40,Rodin Floor 16,Rodin,a10ede1d-044d-4852-87c7-eba7588c2497
41,Rodin Floor 18,Rodin,c3d3f9ae-792c-401c-8bd5-8c61fffe2ab1
42,Rodin Floor 20,Rodin,e88d3561-dce7-4188-89e7-b72cff7d69d6
43,Rodin Floor 22,Rodin,6b7dcd18-fe4e-4dc2-893f-35f0d7939c3c
44,Rodin Floor 24,Rodin,18397cd6-202e-4680-b82e-33ccd9ded1a7
45,Sansom East,Sansom,ad980c78-bf6d-429a-9a08-1b0899f83d62
46,Sansom West,Sansom,d1637690-098b-4eca-b48b-6d137207a38e
47,Stouffer Commons,Stouffer,d4848e7d-fdd0-4faa-b6bd-dc152842cf84
48,New College House,New College House,14b91b75-563b-4a7f-8b80-4efed338c29b
49,Harrison Floor 02,Harrison,78568718-85eb-420b-bc10-77154a685699
50,Van Pelt,Gregory,5d9b0588-c987-4d2c-9842-2ce3e9101577
51,Class of 1925,Gregory,78f20171-ab32-4650-a1ce-28ace7095790
52,Kings Court,KCEH,fdbd6c5f-cb26-486f-86cc-f0b95a7f2a8a
0,Bishop/White,Quad,5faec7e9-a4aa-47c2-a514-950c03fac460
1,Chestnut/Butcher,Quad,7dfa4b34-f44a-4a38-a6b9-44cdb968a915
2,Class of 1928/Fisher,Quad,e6697dca-d164-4980-8843-ea0a29b1cf49
3,Craig,Quad,37d661ce-3e50-4746-ab68-a5c61cd0bd0a
4,DuBois,DuBois,3ffa8978-e742-4076-9bcb-4a3e5c0eca92
5,English House,KCEH,b655a5be-1287-4ce2-b693-e9c1ae526f38
6,Harnwell Floor 02,Harnwell,1c7a9fb3-a938-4756-83c6-42d601d46036
7,Harnwell Floor 04,Harnwell,fba67cc0-336e-42f7-9603-c0b8a0e5030c
8,Harnwell Floor 06,Harnwell,87195ec7-eb3d-42fd-84aa-d63f4e45e285
9,Harnwell Floor 08,Harnwell,1bbb2ff6-d5e6-406d-a3a2-96c7972cceeb
10,Harnwell Floor 10,Harnwell,987bf30b-e8e1-4a9e-b842-c9cd8aeafddc
11,Harnwell Floor 12,Harnwell,dcb76f10-0137-4783-8604-bece4111b6dd
12,Harnwell Floor 14,Harnwell,941b2fcb-2b1b-4afd-8e8e-c100fbcbe0f2
13,Harnwell Floor 16,Harnwell,c74b2798-2d09-42a6-b65c-a5834219be59
14,Harnwell Floor 18,Harnwell,f30af904-72ad-49f6-aecf-f44c8301fb6b
15,Harnwell Floor 20,Harnwell,80a413fd-e0fa-456d-b922-f1576ded1f98
16,Harnwell Floor 22,Harnwell,35119e5e-92c0-45fb-bfeb-f2059196f644
17,Harnwell Floor 24,Harnwell,5880b051-8216-4cf4-92d6-5c7475f43eea
18,Harrison Floor 04,Harrison,447b5682-4c3c-441d-ab49-5f45aee6991f
19,Harrison Floor 06,Harrison,f77f7c68-f719-4843-8987-d64dabc0abff
20,Harrison Floor 08,Harrison,6561bb14-634f-437d-84fd-a0837ef991e7
21,Harrison Floor 10,Harrison,2dd7a63d-7d13-48e5-b038-98054b4f039f
22,Harrison Floor 12,Harrison,fdb607c7-63eb-4d55-a312-0c16b682cbe7
23,Harrison Floor 14,Harrison,53fdd440-e887-49e1-9ca9-7bb3cb4ab541
24,Harrison Floor 16,Harrison,8cedf60a-8f87-4128-89dd-4c75343ca64a
25,Harrison Floor 18,Harrison,116a8d6f-045b-47a5-b3f7-af31f4e661eb
26,Harrison Floor 20,Harrison,f6a8b303-1302-49e6-be53-c8e345316ed8
27,Harrison Floor 22,Harrison,b21c78af-1ebf-418c-a73b-85dc5ff49763
28,Harrison Floor 24,Harrison,9b95c471-053c-46ea-bc3b-d23bcad7a3a1
29,Hill House,Hill,82a00eb7-f70d-4a4c-9f0a-c2dafa4b67ea
30,Magee/Amhurst,Quad,f6825dac-5a5a-4e4b-b66f-ea8226cbe78e
31,Mayer,Stouffer,6e3531d1-eebd-48b4-ad04-cf5983d42b02
32,Morgan,Quad,f249ca9f-ef84-4a35-9477-449b14612057
33,Rodin Floor 02,Rodin,7f25802d-31ad-4f80-ba26-d68a3f403aa8
34,Rodin Floor 04,Rodin,49e560fb-c1aa-4c98-a88a-cc9564481ec0
35,Rodin Floor 06,Rodin,701ce966-aa3c-4063-b3db-548ad89cb643
36,Rodin Floor 08,Rodin,4998a8a2-fb86-4900-bcb7-9d7cc6d9b938
37,Rodin Floor 10,Rodin,030c81c4-2300-4e8e-ae3a-303397a2e216
38,Rodin Floor 12,Rodin,c561f889-5898-41ba-99f5-2e6d4243e4d3
39,Rodin Floor 14,Rodin,2d211700-5b59-4c61-8922-991c0f7d7c15
40,Rodin Floor 16,Rodin,a10ede1d-044d-4852-87c7-eba7588c2497
41,Rodin Floor 18,Rodin,c3d3f9ae-792c-401c-8bd5-8c61fffe2ab1
42,Rodin Floor 20,Rodin,e88d3561-dce7-4188-89e7-b72cff7d69d6
43,Rodin Floor 22,Rodin,6b7dcd18-fe4e-4dc2-893f-35f0d7939c3c
44,Rodin Floor 24,Rodin,18397cd6-202e-4680-b82e-33ccd9ded1a7
45,Sansom East,Sansom,ad980c78-bf6d-429a-9a08-1b0899f83d62
46,Sansom West,Sansom,d1637690-098b-4eca-b48b-6d137207a38e
47,Stouffer Commons,Stouffer,d4848e7d-fdd0-4faa-b6bd-dc152842cf84
48,New College House,New College House,14b91b75-563b-4a7f-8b80-4efed338c29b
49,Harrison Floor 02,Harrison,78568718-85eb-420b-bc10-77154a685699
50,Van Pelt,Gregory,5d9b0588-c987-4d2c-9842-2ce3e9101577
51,Class of 1925,Gregory,78f20171-ab32-4650-a1ce-28ace7095790
52,Kings Court,KCEH,fdbd6c5f-cb26-486f-86cc-f0b95a7f2a8a
124 changes: 57 additions & 67 deletions penn/laundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from bs4 import BeautifulSoup

LAUNDRY_DOMAIN = os.environ.get("LAUNDRY_DOMAIN", "suds.kite.upenn.edu")
ALL_URL = 'http://{}/?location='.format(LAUNDRY_DOMAIN)
ALL_URL = 'http://{}/'.format(LAUNDRY_DOMAIN)
USAGE_BASE_URL = 'https://www.laundryalert.com/cgi-bin/penn6389/LMRoomUsage?CallingPage=LMRoom&Password=penn6389&Halls='


Expand Down Expand Up @@ -82,50 +82,72 @@ def parse_a_hall(self, hall):
:type hall: int
"""
if hall not in self.hall_to_link:
return None # change to to empty json idk
page = requests.get(self.hall_to_link[hall], timeout=60)
return None

return self.parse_halls([hall])[0]

def parse_halls(self, lhall):
"""Return list of names, hall numbers, and the washers/dryers available for a list of halls.

:param halls:
The IDs of the halls to retrieve data for.
:type halls: list(int)
"""
lmachines = []
page = requests.get(ALL_URL)
soup = BeautifulSoup(page.content, 'html.parser')
soup.prettify()
washers = {"open": 0, "running": 0, "out_of_order": 0, "offline": 0, "time_remaining": []}
dryers = {"open": 0, "running": 0, "out_of_order": 0, "offline": 0, "time_remaining": []}

detailed = []

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 = Laundry.update_machine_object(cols, washers)
elif machine_type == "Dryer":
dryers = Laundry.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
})

machines = {"washers": washers, "dryers": dryers, "details": detailed}
return machines
for hall in lhall:
if hall not in self.id_to_hall:
lmachines.append(None)
else:
hall = self.id_to_hall[hall]
washers = {"open": 0, "running": 0, "out_of_order": 0, "offline": 0, "time_remaining": []}
dryers = {"open": 0, "running": 0, "out_of_order": 0, "offline": 0, "time_remaining": []}
found_hall = False
detailed = []

rows = soup.find_all('tr')
for row in rows:
cols = row.find_all('td')
if len(cols) == 1 and len(cols[0].find_all('center')) == 1 and \
len(cols[0].find_all('center')[0].find_all('h2')) == 1: # Title element
if(cols[0].find_all('center')[0].find_all('h2')[0].find_all('a')[0].getText() == hall): # Check if found correct hall
found_hall = True
else:
found_hall = False
elif len(cols) == 5 and cols[2]['class'][0] == 'status' and found_hall: # Content element for relevant hall
machine_type = cols[1].getText()
if machine_type == "Washer":
washers = Laundry.update_machine_object(cols, washers)
elif machine_type == "Dryer":
dryers = Laundry.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
})

machines = {"washers": washers, "dryers": dryers, "details": detailed}
lmachines.append(machines)
return lmachines

def all_status(self):
"""Return names, hall numbers, and the washers/dryers available for all
rooms in the system

>>> all_laundry = l.all_status()
"""
laundry_rooms = {}
for room in self.hall_to_link:
laundry_rooms[room] = self.parse_a_hall(room)
hall_names = list(self.id_to_hall)
hall_values = self.parse_halls(hall_names)

return laundry_rooms
return {k: v for k, v in zip(hall_names, hall_values)}

def hall_status(self, hall_id):
"""Return the status of each specific washer/dryer in a particular
Expand All @@ -149,35 +171,3 @@ def hall_status(self, hall_id):
'hall_name': hall_name,
'location': location
}

def machine_usage(self, hall_no):
"""Returns the average usage of laundry machines every hour
for a given hall.

The usages are returned in a dictionary, with the key being
the day of the week, and the value being an array listing the usages
per hour.

:param hall_no:
integer corresponding to the id number for the hall. Thus number
is returned as part of the all_status call.

>>> english_house = l.machine_usage(2)
"""

try:
num = int(hall_no)
except ValueError:
raise ValueError("Room Number must be integer")
r = requests.get(USAGE_BASE_URL + str(num), timeout=60)
parsed = BeautifulSoup(r.text, 'html5lib')
usage_table = parsed.find_all('table', width='504px')[0]
rows = usage_table.find_all('tr')
usages = {}
for i, row in enumerate(rows):
day = []
hours = row.find_all('td')
for hour in hours:
day.append(self.busy_dict[str(hour['class'][0])])
usages[self.days[i]] = day
return usages
8 changes: 0 additions & 8 deletions tests/laundry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,3 @@ def test_single_hall(self):
ok_('id' in machine)
ok_('type' in machine)
ok_('status' in machine)

def test_usage(self):
for i in range(10):
data = self.laundry.machine_usage(i)
for j in data:
ok_(j in self.laundry.days)
for k in data[j]:
ok_(k in self.laundry.busy_dict.values())