def next_semester_courses(offered_courses, remaining_courses): # Dictionary to store all the courses future_options = {} # Loop through the offered_courses and store their info for courses in __parse_xml(offered_courses).findall(".//LIST_G_COURSE_NO"): for course in courses: id = course.find("COURSE_NO").text.replace("-", "") if id in remaining_courses: course_details = { "CRN": course.find("SSBSECT_CRN").text, "title": course.find("SCBCRSE_TITLE").text, "crd_hr": course.find("SCBCRSE_CREDIT_HR_LOW").text, "max": course.find("SSBSECT_MAX_ENRL").text, "actual": course.find("SSBSECT_ENRL").text } sec_no = 0 for section in __parse_xml(offered_courses).findall(".//LIST_G_SSBSECT_SCHD_CODE"): section_details = { "building": section.find(".//SSRMEET_BLDG_CODE").text, "room": section.find(".//SSRMEET_ROOM_CODE").text, "time": section.find(".//TIME").text, "days": section.find(".//DAYES").text } course_details["section_" + str(sec_no+1)] = section_details future_options[id] = course_details return future_options
def courses_list(response, url=lambda x: x): terms = {} # Loop through courses registered in Blackboard for course in __parse_xml(response).findall( ".//course[@roleIdentifier='S']"): # Extract course key, crn and term string from course identifier course_key, crn, term_string = course.get("courseid").split("_") # Make sure that term id is of the following format "FALL2017" term_string = term_string.split("-")[0] # Split term id to year and semester year, semester = term_string[-4:], __terms[term_string[:3]] # Get term full name of the following format "Fall 2017-2018" term_name = f"{semester['name']} {year}-{int(year) + 1}" # If term hasn't been added yet if term_name not in terms: # Initialize it with an empty dictionary terms[term_name] = {} # Add course to the correspondent term terms[term_name][__clean(course.get("name"))] = { # Content links to Blackboard's documents and deadlines "Content": url(course_key + "/" + course.get("bbid")[1:-2]), # Details links to MyUDC's course details "Details": url(f"{course_key}/{crn}/{year + semester['code']}") } return terms
def schedule(response): # Dictionary to store schedule details details = {} # Loop through courses in the schedule report to get its details for course in __parse_xml(response).find(".//LIST_G_SSBSECT_CRN"): course_id = course.find("SSBSECT_SUBJ_CODE").text + course.find("SSBSECT_CRSE_NUMB").text # Scrape and store data dictionary in details["course id"] details[course_id] = { "crn": course.find("SSBSECT_CRN").text, "section": course.find("SSBSECT_SEQ_NUMB").text, "type": course.find("SSBSECT_SCHD_CODE").text, "credits": course.find("SFRSTCR_CREDIT_HR").text, "title": course.find("SCBCRSE_TITLE").text.strip(), # Initialize an empty classes array for later "classes": [] } # Loop through days in the current course for day in course.find("LIST_G_DAYES"): # Add a class dictionary of data for every day in course details[course_id]["classes"].append({ "days": day.find("DAYES").text.split(), "time": day.find("TIME").text.split(" - "), "building": day.find("SSRMEET_BLDG_CODE").text, "room": day.find("SSRMEET_ROOM_CODE").text, # Place doctors in an array as there might be many "doctor": [ doctor.find("CF_INSTRUCTOR_NAME").text # Loop through doctor in the current day for doctor in day.find("LIST_G_SIRASGN_PIDM") ] }) return details
def values_of_majors(schedules): return { # Add student's major from his schedule as {"major name": "major initials"} schedule.find("CF_MAJR_DESC").text: schedule.find("SGBSTDN_MAJR_CODE_1").text # Loop through each schedule from schedules report for schedule in __parse_xml(schedules).find("LIST_G_STVCOLL_DESC") }
def grades_and_gpa(transcript, term_code): # Parse xml and declare variables xml = __parse_xml(transcript) try: quality = float(xml.find(".//SHRLGPA_QUALITY_POINTS").text) hours = int(xml.find(".//SHRLGPA_GPA_HOURS").text) except AttributeError: quality, hours = 0, 0 all_hours = hours term_quality = 0 grades = [] # Loop through the available terms in transcript terms = xml.find(".//LIST_G_ACADEMIC_HIST_TERM") for term in terms if terms is not None else (): # If the term is the selected one if term.find("TERM_CODE_KEY").text == term_code: # Loop through courses in that term for course in term.find("LIST_G_ACADEMIC_HIST_DETAILS"): # Store its credit hours, grade and key crhrs = int(course.find("CREDIT_HOURS").text) grade = course.find("GRDE_CODE_FINAL").text key = course.find("SUBJ_CODE").text[-7:] # Add them to total hours and term quality all_hours += crhrs term_quality += __to_gpa(grade, 0) * crhrs # Add course's (key, title, grade) to the grades list grades.append((key, course.find("COURSE_TITLE").text, grade)) # Calculate and return new grades and (term, new and old GPA) return grades, { "term": term_quality / (all_hours - hours) if all_hours != hours else 0, "new": (term_quality + quality) / all_hours if all_hours else 0, "old": quality / hours if hours else 0 }
def courses_dictionary(response): return { # Store courses ids in {Blackboard id: MyUDC id} pairs course.get("bbid")[1:-2]: course.get("courseid").split("_", 1)[0] # Loop through all courses in Blackboard in which the user is a student for course in __parse_xml(response).findall( ".//course[@roleIdentifier='S']") }
def study_plan_courses(study_plan): # Dictionary to store all the courses all_courses = set() # Loop through the study_plan and store their info for term in __parse_xml(study_plan).findall(".//LIST_G_GROUP_COURSES"): for course in term: if course.find("CF_IS_PASSED").text == 'N': all_courses.add(course.find("COURSE_NUMB1").text) return all_courses
def remaining_courses(study_plan): plan = __parse_xml(study_plan) return { "crhrs": int(plan.find(".//SMBPOGN_REQ_CREDITS_OVERALL").text) - int(plan.find(".//SMBPOGN_ACT_CREDITS_OVERALL").text), "courses": [ course.find("COURSE_NUMB1").text for course in plan.findall(".//G_GROUP_COURSES") if course.find("CF_IS_PASSED").text == "N" ] }
def terms_list(response): terms = {} # Loop through courses registered in Blackboard for course in __parse_xml(response).findall( ".//course[@roleIdentifier='S']"): # Extract term's string from course id in "FALL2017" format term_string = course.get("courseid").rsplit("_", 1)[-1].split("-")[0] # Split term id to year and term semester year, semester = term_string[-4:], __terms[term_string[:3]] # Store term in terms in {"Fall 2017-2018": "201710"} pairs terms[f"{semester['name']} {year}-{int(year) + 1}"] = year + semester[ "code"] return terms
def values_of(courses, *params): # For each supported value, create an empty dictionary in values for later use values = {param: {} for param in params if param in ["Campus", "College", "Department"]} # Loop through courses to get the required values for course in __parse_xml(courses).find("LIST_G_SSBSECT_TERM_CODE"): # If campus values are required if "Campus" in values: # Add course's campus to values as {"campus name": "campus abbreviation"} values["Campus"].update({course.find("CAMPUS_DESC").text: course.find("SSBSECT_CAMP_CODE").text}) # If college values are required if "College" in values: # Add course's college to values as {"college name": "college number"} values["College"].update({course.find("COLLEGE_NAME").text: course.find("SCBCRSE_COLL_CODE").text}) # If department values are required if "Department" in values: # Add course's department to values as {"department name": "department initials"} values["Department"].update({course.find("DEPT_NAME").text: course.find("SCBCRSE_DEPT_CODE").text}) return values
def courses_by_term(response, term_code): courses = {} # Get Blackboard term string in "FALL2017" format from term code term_string = re.compile("^" + __terms[term_code[4:]]["name"] + "[A-Z]*" + term_code[:4]) # Loop through list of courses in parsed XML for course in __parse_xml(response).find("courses"): # Store course's MyUDC id, CRN and term name key, crn, full_term = course.get("courseid").split("_") # Only add courses in the requested term and in which the user is a student if term_string.match(full_term) and course.get( "roleIdentifier") == "S": # Add course ids in {MyUDC id: Blackboard id} pairs courses[key] = { # Store course's Blackboard id "courseId": course.get("bbid")[1:-2], "crn": crn } return courses
def course_data(response, course_key, course_id, data_type=None): # Store parsed course and returned object structure course = __parse_xml(response) data = {"deadlines": [], "documents": []} # If requested data type isn't "documents" if data_type != "documents": # Scrape deadlines and add them to data data["deadlines"] = [ { # Store deadline's title, due date & other information "title": deadline.get("name"), "dueDate": deadline.get("dueDate"), "time": deadline.get("createdDate"), "course": course_key, "courseId": course_id, "contentId": deadline.get("contentid")[1:-2] } # Loop through all course items which have a due date for deadline in course.findall(".//*[@dueToday]") ] # If requested data type is "deadlines" if data_type == "deadlines": # Only return the deadlines return data["deadlines"] # If requested data type isn't "deadlines" if data_type != "deadlines": # Scrape documents and add them to data data["documents"] = [ { # Extract document xid and content id using Regex from its URL "id": "_".join(__document_ids.search(document.get("url")).groups()), # Store document's title, upload date & course key "course": course_key, "title": document.getparent().getparent().get("name"), "file": document.get("name"), "time": document.get("modifiedDate"), } # Loop through all course documents (not more than 25) for document in (course.findall(".//attachment") or [])[:25] ] # If requested data type is "documents" if data_type == "documents": # Only return the documents return data["documents"] # Return everything if data type isn't specified or invalid return data
def scrape_course_grades(response, course_key): grades = [] # Loop through grades in available in course for grade in __parse_xml(response).find("grades"): print(grade.get("name")) # Store last modified time time = grade.get("lastInstructorActivity") # If it's a graded items if time: # Add grade dictionary to grades grades.append({ # Add item's title, grade & course key "course": course_key, "title": grade.get("name"), "grade": ceil(float(grade.get("grade"))), # Add total grade and uploaded time "outOf": ceil(float(grade.get("pointspossible"))), "time": time }) return grades
def core_details(transcript): # Find and store the element which contains the required info soup = __parse_xml(transcript).find(".//G_SGBSTDN") # Create a dictionary of straight forward to reach info details = { # Store student's name, college, major and terms "name": soup.find("STUDENT_NAME").text.strip(), "college": soup.find("CURR_COLL_CODE").text, "major": soup.find("CURR_MAJR_CODE").text, "terms": { # Initialize in progress term "in_progress": {}, # Store all terms key "all_keys": [ # Place term key in the array term.find("TERM_CODE_KEY").text # Loop through terms which aren't in progress for term in soup.find(".//LIST_G_ACADEMIC_HIST_TERM") ] }, # Store student's first term to be used in offered_courses() "first_term": soup.find("FIRST_TERM_ADMIT").text } # Loop through in progress terms and keep the index of looping for index, term in enumerate(soup.find("LIST_G_SFRSTCR_PIDM")): # Add in progress term's key to all terms key details["terms"]["all_keys"].append(term.find("SFRSTCR_TERM_CODE").text) # If it's the first in progress term if index == 0: # Store its key and courses as the "in_progress" term details["terms"]["in_progress"][term.find("SFRSTCR_TERM_CODE").text] = { # Combine course's subject code and section code to get its key course.find("SSBSECT_SUBJ_CODE").text + course.find("SSBSECT_CRSE_NUMB").text: # With the line above, form {"course key": "course name"} pairs course.find("SFRSTCR_COURSE_TITLE").text.strip() # Loop through courses in that term for course in term.find("LIST_G_SFRSTCR_DETAIL") } return details