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

Added Study Room Functions to to Library Module #177

Merged
merged 37 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
822aa50
Added a couple Hillman library features
ghosteau Jun 9, 2024
b935689
fixed a bug with reservations pulling; moved reservations functions t…
ghosteau Jun 9, 2024
ea0d2c5
revert lab back
ghosteau Jun 9, 2024
0455c9d
revert lab
ghosteau Jun 9, 2024
12e9c85
Update lab.py
ghosteau Jun 9, 2024
8d830ad
revert lab again
ghosteau Jun 9, 2024
5707e24
Merge branch 'dev' into in_prog
tianyizheng02 Jun 9, 2024
a16f118
Ran black to format correctly
ghosteau Jun 9, 2024
70efe5c
refromatted with black
ghosteau Jun 9, 2024
ad50360
Update pittapi/course.py
ghosteau Jun 10, 2024
d43e986
Delete black
ghosteau Jun 10, 2024
4b27e5e
Update course_test.py
ghosteau Jun 10, 2024
b4c764f
Update dining_test.py
ghosteau Jun 10, 2024
d2c4144
Update library_test.py
ghosteau Jun 10, 2024
f7f7cd0
Update course_mocks.py
ghosteau Jun 10, 2024
fb9db3b
Update people_test.py
ghosteau Jun 10, 2024
c85481e
Update sports_test.py
ghosteau Jun 10, 2024
05e2e3e
Update status_test.py
ghosteau Jun 10, 2024
29c41c9
Update textbook_test.py
ghosteau Jun 10, 2024
7e99d74
Update textbook_test.py
ghosteau Jun 10, 2024
cb200d7
Update course_mocks.py
ghosteau Jun 11, 2024
25cf627
Update dining_test.py
ghosteau Jun 11, 2024
ecd3d3e
Update course_test.py
ghosteau Jun 11, 2024
de7267d
Update textbook_test.py
ghosteau Jun 11, 2024
b4650e5
Update course_mocks.py
ghosteau Jun 11, 2024
6fa494b
Fix black errors
tianyizheng02 Jun 11, 2024
5e0e14e
Update library.py
ghosteau Jun 11, 2024
fecd07e
Added type hints for new functions
ghosteau Jun 12, 2024
309237f
Polished up formatting in library file
ghosteau Jun 13, 2024
642c99d
Merge branch 'dev' into in_prog
ghosteau Jun 13, 2024
ce237fc
Added unit tests for library module update
ghosteau Jun 13, 2024
5471dd8
Merge branch 'in_prog' of https://github.com/ghosteau/PittAPI into in…
ghosteau Jun 13, 2024
433d00f
changed spacing for a line
ghosteau Jun 13, 2024
32185c4
Update tests/library_test.py
ghosteau Jun 13, 2024
a35cd68
Update tests/library_test.py
ghosteau Jun 13, 2024
0cea09f
Update tests/library_test.py
ghosteau Jun 13, 2024
eb0c456
Update type hints
tianyizheng02 Jun 13, 2024
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
Empty file added black
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
Empty file.
20 changes: 15 additions & 5 deletions pittapi/cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,21 @@ class Event(NamedTuple):
meta: str


ACADEMIC_CALENDAR_URL: str = "https://25livepub.collegenet.com/calendars/pitt-academic-calendar.json"
GRADES_CALENDAR_URL: str = "https://25livepub.collegenet.com/calendars/pitt-grades-calendar.json"
ENROLLMENT_CALENDAR_URL: str = "https://25livepub.collegenet.com/calendars/pitt-enrollment-calendar.json"
COURSE_CALENDAR_URL: str = "https://25livepub.collegenet.com/calendars/pitt-courseclass-calendar.json"
GRADUATION_CALENDAR_URL: str = "https://25livepub.collegenet.com/calendars/pitt-graduation-calendar.json"
ACADEMIC_CALENDAR_URL: str = (
"https://25livepub.collegenet.com/calendars/pitt-academic-calendar.json"
)
GRADES_CALENDAR_URL: str = (
"https://25livepub.collegenet.com/calendars/pitt-grades-calendar.json"
)
ENROLLMENT_CALENDAR_URL: str = (
"https://25livepub.collegenet.com/calendars/pitt-enrollment-calendar.json"
)
COURSE_CALENDAR_URL: str = (
"https://25livepub.collegenet.com/calendars/pitt-courseclass-calendar.json"
)
GRADUATION_CALENDAR_URL: str = (
"https://25livepub.collegenet.com/calendars/pitt-graduation-calendar.json"
)


def _fetch_calendar_events(url: str) -> List[Event]:
Expand Down
9 changes: 9 additions & 0 deletions pittapi/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,16 @@ def get_course_details(term: Union[str, int], subject: str, course: Union[str, i
)

return CourseDetails(
<<<<<<< HEAD
course=Course(
subject_code=subject,
course_number=course,
course_id=internal_course_id,
course_title=course_title,
),
=======
course=Course(subject_code=subject, course_number=course, course_id=internal_course_id, course_title=course_title),
>>>>>>> 5707e24e2f881755bb429b3b1a0f2245940b46d6
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
course_description=course_description,
credit_range=credit_range,
requisites=requisites,
Expand Down
10 changes: 9 additions & 1 deletion pittapi/lab.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,13 @@ def get_status():
if key["name"] in labs:
total = key["total"]
in_use = key["active"]
statuses.append({"location": key["name"], "isOpen": labs[key["name"]], "total": total, "in_use": in_use})
statuses.append(
{
"location": key["name"],
"isOpen": labs[key["name"]],
"total": total,
"in_use": in_use,
}
)

return statuses
4 changes: 3 additions & 1 deletion pittapi/laundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@

from bs4 import BeautifulSoup

BASE_URL = "https://www.laundryview.com/api/currentRoomData?school_desc_key=197&location={}"
BASE_URL = (
"https://www.laundryview.com/api/currentRoomData?school_desc_key=197&location={}"
)

LOCATION_LOOKUP = {
"TOWERS": "2430136",
Expand Down
46 changes: 44 additions & 2 deletions pittapi/library.py
tianyizheng02 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
"&sort=rank&tab=Everything&vid=01PITT_INST:01PITT_INST"
)

STUDY_ROOMS_URL = "https://pitt.libcal.com/spaces/bookings/search?lid=917&gid=1558&eid=0&seat=0&d=1&customDate=&q=&daily=0&draw=1&order%5B0%5D%5Bcolumn%5D=1&order%5B0%5D%5Bdir%5D=asc&start=0&length=25&search%5Bvalue%5D=&_=1717907260661"


QUERY_START = "&q=any,contains,"

sess = requests.session()
Expand Down Expand Up @@ -119,11 +122,50 @@ def _extract_documents(documents: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
return new_docs


def _extract_facets(facet_fields: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]:
def _extract_facets(
facet_fields: List[Dict[str, Any]]
) -> Dict[str, List[Dict[str, Any]]]:
facets = {} # type: Dict[str,List[Dict[str,Any]]]
for facet in facet_fields:
facets[facet["display_name"]] = []
for count in facet["counts"]:
facets[facet["display_name"]].append({"value": count["value"], "count": count["count"]})
facets[facet["display_name"]].append(
{"value": count["value"], "count": count["count"]}
)

return facets


def hillman_total_reserved():
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
"""Returns a simple count dictionary of the total amount of reserved rooms appointments"""
count = {}
resp = requests.get(STUDY_ROOMS_URL)
resp = resp.json()
# Total records is kept track of by default in the JSON
total_records = resp["recordsTotal"]

# Note: this must align with the amount of entries in reserved times function; renamed for further clarification
count["Total Hillman Reservations"] = total_records
return count


def reserved_hillman_times():
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
"""Returns a list of dictionaries of reserved rooms of the Hillman with their respective times"""
bookings = []

resp = requests.get(STUDY_ROOMS_URL)
resp = resp.json()
data = resp["data"]

if data is None:
return bookings

# Note: there can be multiple reservations in the same room, hence why we must use a list of maps, and cannot just use a singular map
for reservation in data:
bookings.append(
{
"Room": reservation["itemName"],
"Reserved": [reservation["from"], reservation["to"]],
}
)
return bookings
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 3 additions & 1 deletion pittapi/news.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def _load_n_items(feed: str, max_news_items: int):
request_objs = []
for i in range(int(math.ceil(max_news_items / 10))):
payload["start"] = i * 10
request_objs.append(grequests.get("https://m.pitt.edu/news/index.json", params=payload))
request_objs.append(
grequests.get("https://m.pitt.edu/news/index.json", params=payload)
)

responses = grequests.imap(request_objs)

Expand Down
8 changes: 6 additions & 2 deletions pittapi/shuttle.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ def get_map_vehicle_points(api_key: str = "8882812681") -> Dict[str, Any]:
return response.json()


def get_route_stop_arrivals(api_key: str = "8882812681", times_per_stop: int = 1) -> Dict[str, Any]:
def get_route_stop_arrivals(
api_key: str = "8882812681", times_per_stop: int = 1
) -> Dict[str, Any]:
"""Return stop arrival times for all vehicles."""
payload = {"ApiKey": api_key, "TimesPerStopString": times_per_stop}
response = sess.get(
Expand All @@ -43,7 +45,9 @@ def get_route_stop_arrivals(api_key: str = "8882812681", times_per_stop: int = 1
return response.json()


def get_vehicle_route_stop_estimates(vehicle_id: str, quantity: int = 2) -> Dict[str, Any]:
def get_vehicle_route_stop_estimates(
vehicle_id: str, quantity: int = 2
) -> Dict[str, Any]:
"""Return {quantity} stop estimates for all active vehicles."""
payload = {"vehicleIdStrings": vehicle_id, "quantity": str(quantity)}
response = sess.get(
Expand Down
14 changes: 11 additions & 3 deletions pittapi/sports.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

import requests

FOOTBALL_URL = "http://site.api.espn.com/apis/site/v2/sports/football/college-football/teams/pitt"
FOOTBALL_URL = (
"http://site.api.espn.com/apis/site/v2/sports/football/college-football/teams/pitt"
)
MENS_BASKETBALL_URL = "http://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/teams/pittsburgh"


Expand Down Expand Up @@ -47,7 +49,10 @@ def get_next_mens_basketball_game() -> dict:
status = None
if next_game["competitions"][0]["status"]["type"]["name"] == "STATUS_FINAL":
status = "GAME_COMPLETE"
elif next_game["competitions"][0]["status"]["type"]["name"] == "STATUS_IN_PROGRESS":
elif (
next_game["competitions"][0]["status"]["type"]["name"]
== "STATUS_IN_PROGRESS"
):
status = "IN_PROGRESS"
if next_game["competitions"][0]["competitors"][0]["id"] == 221:
opponent = next_game["competitions"][0]["competitors"][0]
Expand Down Expand Up @@ -105,7 +110,10 @@ def get_next_football_game() -> dict:
status = None
if next_game["competitions"][0]["status"]["type"]["name"] == "STATUS_FINAL":
status = "GAME_COMPLETE"
elif next_game["competitions"][0]["status"]["type"]["name"] == "STATUS_IN_PROGRESS":
elif (
next_game["competitions"][0]["status"]["type"]["name"]
== "STATUS_IN_PROGRESS"
):
status = "IN_PROGRESS"
if next_game["competitions"][0]["competitors"][0]["id"] == 221:
opponent = next_game["competitions"][0]["competitors"][1]
Expand Down
41 changes: 33 additions & 8 deletions pittapi/textbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,15 +294,26 @@ def _extract_id(response, course: str, instructor: str, section: str) -> str:
return _find_course_id_by_instructor(sections, instructor.upper())
except LookupError:
error += 2
raise LookupError("Unable to find course by " + LOOKUP_ERRORS[error].format(section, instructor))
raise LookupError(
"Unable to find course by " + LOOKUP_ERRORS[error].format(section, instructor)
)


def _extract_books(ids: List[str]) -> List[Dict[str, str]]:
"""Fetches a course's textbook information and returns a list
of textbooks for the given course.
"""
responses = grequests.imap([grequests.get(BASE_URL + _construct_query("books", section_id)) for section_id in ids])
books = [_filter_dictionary(book, KEYS) for response in responses for book in response.json()]
responses = grequests.imap(
[
grequests.get(BASE_URL + _construct_query("books", section_id))
for section_id in ids
]
)
books = [
_filter_dictionary(book, KEYS)
for response in responses
for book in response.json()
]
return books


Expand Down Expand Up @@ -336,7 +347,9 @@ def _get_department_number(department_code: str) -> int:
if department_number > 22462:
department_number += 2 # between codes DSANE and EAS 2 id numbers are skipped.
if department_number > 22580:
department_number += 1 # between codes PUBSRV and REHSCI 1 id number is skipped.
department_number += (
1 # between codes PUBSRV and REHSCI 1 id number is skipped.
)
return department_number


Expand All @@ -346,7 +359,10 @@ def get_textbooks(term: str, courses: List[Dict[str, str]]) -> List[Dict[str, st
responses = grequests.map(
[
grequests.get(
BASE_URL + _construct_query("courses", _get_department_number(department), _validate_term(term)),
BASE_URL
+ _construct_query(
"courses", _get_department_number(department), _validate_term(term)
),
timeout=10,
)
for department in departments
Expand All @@ -367,11 +383,20 @@ def get_textbooks(term: str, courses: List[Dict[str, str]]) -> List[Dict[str, st
return _extract_books(section_ids)


def get_textbook(term: str, department: str, course: str, instructor: str = None, section: str = None) -> List[Dict[str, str]]:
def get_textbook(
term: str, department: str, course: str, instructor: str = None, section: str = None
) -> List[Dict[str, str]]:
"""Retrieves textbooks for a given course."""
has_section_or_instructor = (instructor is not None) or (section is not None)
if not has_section_or_instructor:
raise TypeError("get_textbook() is missing a instructor or section argument")
response = requests.get(BASE_URL + _construct_query("courses", _get_department_number(department), _validate_term(term)))
section_id = _extract_id(response, department + _validate_course(course), instructor, section)
response = requests.get(
BASE_URL
+ _construct_query(
"courses", _get_department_number(department), _validate_term(term)
)
)
section_id = _extract_id(
response, department + _validate_course(course), instructor, section
)
return _extract_books([section_id])
36 changes: 32 additions & 4 deletions tests/course_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,21 @@
from unittest.mock import MagicMock

from pittapi import course
<<<<<<< HEAD
from pittapi.course import (
Attribute,
Component,
Course,
CourseDetails,
Instructor,
Meeting,
Section,
SectionDetails,
Subject,
)
=======
from pittapi.course import Attribute, Component, Course, CourseDetails, Instructor, Meeting, Section, SectionDetails, Subject
>>>>>>> 5707e24e2f881755bb429b3b1a0f2245940b46d6
from tests.mocks.course_mocks import (
mocked_subject_data,
mocked_courses_data,
Expand All @@ -35,7 +49,9 @@
class CourseTest(unittest.TestCase):
def setUp(self):
course._get_subjects = MagicMock(return_value=mocked_subject_data)
course._get_section_details = MagicMock(return_value=mocked_section_details_data)
course._get_section_details = MagicMock(
return_value=mocked_section_details_data
)

def test_validate_term(self):
# If convert to string
Expand Down Expand Up @@ -85,15 +101,19 @@ def test_get_subject_courses(self):
self.assertTrue(isinstance(test_course, Course))

def test_get_subject_courses_invalid(self):
course._get_subject_courses = MagicMock(return_value=mocked_courses_data_invalid)
course._get_subject_courses = MagicMock(
return_value=mocked_courses_data_invalid
)

self.assertRaises(ValueError, course.get_subject_courses, "nonsense")
course._get_subject_courses.assert_not_called()

def test_get_course_details(self):
course._get_course_id = MagicMock(return_value="105611")
course._get_course_info = MagicMock(return_value=mocked_course_info_data)
course._get_course_sections = MagicMock(return_value=mocked_course_sections_data)
course._get_course_sections = MagicMock(
return_value=mocked_course_sections_data
)

course_sections = course.get_course_details("2231", "CS", "0007")

Expand All @@ -110,7 +130,13 @@ def test_get_course_details(self):
test_attribute = course_sections.attributes[0]
self.assertTrue(isinstance(test_attribute, Attribute))
self.assertEqual(test_attribute.attribute, "DSGE")
<<<<<<< HEAD
self.assertEqual(
test_attribute.attribute_description, "*DSAS General Ed. Requirements"
)
=======
self.assertEqual(test_attribute.attribute_description, "*DSAS General Ed. Requirements")
>>>>>>> 5707e24e2f881755bb429b3b1a0f2245940b46d6
self.assertEqual(test_attribute.value, "ALG")

self.assertEqual(test_attribute.value_description, "Algebra")
Expand Down Expand Up @@ -142,7 +168,9 @@ def test_get_course_details(self):
self.assertEqual(test_instructor.name, "Robert Fishel")

def test_get_section_details(self):
course._get_section_details = MagicMock(return_value=mocked_section_details_data)
course._get_section_details = MagicMock(
return_value=mocked_section_details_data
)

section_details = course.get_section_details("2231", "27815")

Expand Down
11 changes: 10 additions & 1 deletion tests/dining_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ def test_get_location_hours(self):
status=200,
)

self.assertIsInstance(dining.get_location_hours("The Eatery", datetime.datetime(2024, 4, 12)), dict)
self.assertIsInstance(
dining.get_location_hours("The Eatery", datetime.datetime(2024, 4, 12)),
dict,
)

@responses.activate
def test_get_location_menu(self):
Expand All @@ -82,5 +85,11 @@ def test_get_location_menu(self):
json=self.dining_menu_data,
status=200,
)
<<<<<<< HEAD
locations = dining.get_location_menu(
"The Eatery", datetime.datetime(2024, 4, 12), "Breakfast"
)
=======
locations = dining.get_location_menu("The Eatery", datetime.datetime(2024, 4, 12), "Breakfast")
>>>>>>> 5707e24e2f881755bb429b3b1a0f2245940b46d6
self.assertIsInstance(locations, dict)
ghosteau marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions tests/library_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ def __init__(self, *args, **kwargs):

@responses.activate
def test_get_documents(self):
<<<<<<< HEAD
responses.add(
responses.GET,
library.LIBRARY_URL + library.QUERY_START + "water",
json=self.library_query,
status=200,
)
=======
responses.add(responses.GET, library.LIBRARY_URL + library.QUERY_START + "water", json=self.library_query, status=200)
>>>>>>> 5707e24e2f881755bb429b3b1a0f2245940b46d6
query_result = library.get_documents("water")
self.assertIsInstance(query_result, dict)
self.assertEqual(query_result["pages"], 10)
Expand Down
Loading