123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- """
- Haiku (PowerSchool Learning) backend
- This file is part of polyglot, etc.
- """
- from lxml.html import parse
- from io import StringIO
- from backends.support import Backend, Course, ResourceCategory, ResourceRawHTML, FileUpload, Quiz, MultipleChoiceQuestion, FreeResponseQuestion, Forum, Post
- import authentication.google
- import requests
- import re
- class HaikuResourceXHR(ResourceRawHTML):
- def __init__(self, name, b_id, backend, course, referer):
- self.name = name
- self.b_id = b_id
- self.backend = backend
- self.course = course
- self.referer = referer
- @property
- def html(self):
- url = self.backend.base + self.course.id + "/cms_box/render_content/" + self.b_id
- s = self.backend.session.post(url, {"csrf_token": self.backend.csrftok}, headers = {
- "X-Requested-With": "XMLHttpRequest",
- "X-Prototype-Version": "1.7",
- "Referer": self.referer
- })
- return s.text
- class HaikuResourceCategory(ResourceCategory):
- def __init__(self, name, url, children, backend, course):
- self.name = name
- self.url = url
- self.children = children
- self.backend = backend
- self.course = course
- @property
- def contents(self):
- ret = []
- out = self.backend.session.get(self.backend.base + self.url)
- tree = parse(StringIO(out.text)).getroot()
- for box in tree.cssselect(".cms_box"):
- name = box.text_content().strip()
- b_id = box.get("id")[len("box_"):]
- ret += [HaikuResourceXHR(name, b_id, self.backend, self.course, self.backend.base + self.url)]
- return ret
- """
- class HaikuQuiz(Quiz):
- def __init__(self, course, id, name):
- super().__init__(id, name)
- self.course = course
- def questions(self):
- question1 = MultipleChoiceQuestion("Is mathematics fun?", ["Absolutely!", "Definitely!"])
- question2 = FreeResponseQuestion("What's your favourite part?")
- return [question1, question2]
- def submit(self, responses):
- return True
- class HaikuForum(Forum):
- def __init__(self, course, id, name):
- super().__init__(id, name)
- self.course = course
- @property
- def thread(self):
- reply2 = Post("Babs Seed", "That gives me a good idea to annoy the CMCs!", [])
- reply1 = Post("Jane Doe", "DNA nanotechnology involves forming artificial, designed nanostructures", [reply2])
- question = Post("Carl Smith", "In the homework problem set, you looked at a formula to estimate the number of basepairs in a DNA strand given the length. What are your thoughts?", [reply1])
- return question
- def reply(self, parent, body):
- return True
- """
- class HaikuCourse(Course):
- def __init__(self, backend, course):
- self.backend = backend
- # onclick ala redirect_to('/teacher/coursestring/cms_page/view')
- # why they can't just use an <a> is a mystery o_o
- self.url = course.get("onclick")[len("redirect_to('"):-(len("')"))]
- self.id = self.url[:-len("/cms_page/view")]
- # Course name and teacher name, no localisation?
- self.title = course.text_content().strip()
- self.teacher = "Unknown teacher" # TODO?
- def course_page(self):
- return parse(StringIO(self.backend.session.get(self.backend.base + self.url).text)).getroot()
- """
- There are a *lot* of resources Haiku emits.
- The most basic are found in the sidebar (#sidebar)
- Take the links and interpret them semantically based on list tags (reasonable HTML, huh?!)
- Deeper nested resources are more links away (and XHR); careful about bandwidth!
- """
- @property
- def resources(self):
- def process_li(li):
- name = ""
- href = ""
- children = []
- for child in li:
- if child.tag == "div":
- link = child.cssselect("a")[0]
- name = link.text_content().strip()
- href = link.get("href")
- elif child.tag == "ul":
- children = [process_li(li) for li in child]
- return HaikuResourceCategory(name, href, children, self.backend, self)
- sidebar = self.course_page().cssselect("#sidebar")[0][0]
- return ResourceCategory("Root", process_ul(sidebar), [])
- class HaikuBackend(Backend):
- Config = ["Email", "Password", "School"]
- def login(self, config):
- self.session = requests.Session()
- self.base = "https://" + config["School"] + ".learning.powerschool.com"
- endpoint = self.base + "/do/authentication/google/google_begin?&openid_identifier=" + config["Email"].split("@")[1]
- out = GoogleAuth.oauth(self.session, config["Email"], config["Password"], endpoint)
- self.portal = parse(StringIO(out.text))
- self.csrftok = re.search("^ CSRFTOK = '([0-9a-f]*)';$", out.text, re.MULTILINE).group(1)
- return True # TODO
- @property
- def courses(self):
- courses = []
- for course in self.portal.getroot().cssselect(".filter-class"):
- courses += [HaikuCourse(self, course)]
- return courses
|