Example #1
0
def setup_new_class():
    """Setup a new class based on a zyBooks class code"""
    window = ui.get_window()
    zy_api = Zybooks()

    text_input = ui.layers.TextInputLayer("Class Code")
    text_input.set_prompt(["Enter class code"])
    window.run_layer(text_input)
    if text_input.canceled:
        return

    # Check if class code is valid
    code = text_input.get_text()
    valid = zy_api.check_valid_class(code)
    if valid:
        popup = ui.layers.Popup("Valid", [f"{code} is valid"])
        window.run_layer(popup)
    else:
        popup = ui.layers.Popup("Invalid", [f"{code} is invalid"])
        window.run_layer(popup)
        return

    # If code is valid, add it to the shared configuration
    SharedData.add_class(code)

    # Download the list of students
    roster = zy_api.get_roster()

    save_roster(roster)
    popup = ui.layers.Popup("Finished",
                            ["Successfully downloaded student roster"])
    window.run_layer(popup)
    class_section_manager()
Example #2
0
def get_submission(lab, student, use_locks=True):
    """Get a submission from zyBooks given the lab and student"""
    window = ui.get_window()
    zy_api = Zybooks()

    # Lock student
    if use_locks:
        data.lock.lock(student, lab)

    submission_response = zy_api.download_assignment(student, lab)
    submission = data.model.Submission(student, lab, submission_response)

    # Report missing files
    if submission.flag & data.model.SubmissionFlag.BAD_ZIP_URL:
        msg = [
            f"One or more URLs for {student.full_name}'s code submission are bad.",
            "Some files could not be downloaded. Please",
            "View the most recent submission on zyBooks.",
        ]
        popup = ui.layers("Warning", msg)
        window.run_layer(popup)

    # A student may have submissions beyond the due date, and an exception
    # In case that happens, always allow a normal grade, but show a message
    if submission.flag == data.model.SubmissionFlag.NO_SUBMISSION:
        pass

    return submission
Example #3
0
def download_roster(silent=False):
    """Download the roster of students from zybooks and save to disk"""
    window = ui.get_window()
    zy_api = Zybooks()

    roster = zy_api.get_roster()

    if not silent and not roster:
        popup = ui.layers.Popup("Failed",
                                ["Failed to download student roster"])
        window.run_layer(popup)
        return
    if roster:
        save_roster(roster)
    if not silent:
        popup = ui.layers.Popup("Finished",
                                ["Successfully downloaded student roster"])
        window.run_layer(popup)
Example #4
0
def fetch_zybooks_toc():
    window = ui.get_window()
    zy_api = Zybooks()

    popup = ui.layers.WaitPopup("Table of Contents",
                                ["Fetching TOC from zyBooks"])
    popup.set_wait_fn(zy_api.get_table_of_contents)
    window.run_layer(popup)

    return popup.get_result()
Example #5
0
def submission_search_fn(logger, lab, search_string, output_path, use_regex):
    students = data.get_students()
    zy_api = Zybooks()

    regex_str = search_string if use_regex else re.escape(search_string)
    search_pattern = re.compile(regex_str)

    with open(output_path, "w", newline="") as log_file:
        csv_log = csv.DictWriter(log_file,
                                 fieldnames=[
                                     "Name", "Submission",
                                     (f"(Searching for {search_string})"
                                      f"{' as a regex' if use_regex else ''}")
                                 ])
        csv_log.writeheader()
        student_num = 1

        for student in students:
            while True:
                counter = f"[{student_num}/{len(students)}]"
                logger.log(f"{counter:12} Checking {student.full_name}")

                match_result = check_student_submissions(
                    zy_api, str(student.id), lab, search_pattern)

                if match_result["code"] == Zybooks.DOWNLOAD_TIMEOUT:
                    logger.log(
                        "Download timed out... trying again after a few seconds"
                    )
                    time.sleep(5)
                else:
                    break

            if match_result["code"] == Zybooks.NO_ERROR:
                csv_log.writerow({
                    "Name": student.full_name,
                    "Submission": match_result['time']
                })

                logger.append(f" found {search_string}")

            # Check for and log errors
            if "error" in match_result:
                csv_log.writerow({
                    "Name":
                    student.full_name,
                    "Submission":
                    f"ERROR: {match_result['error']}"
                })

            student_num += 1
Example #6
0
def add_lab():
    """Add a lab to the current class"""
    window = ui.get_window()
    zy_api = Zybooks()

    text_input = ui.layers.TextInputLayer("Lab Name")
    text_input.set_prompt(["Enter the Lab Name"])
    window.run_layer(text_input)
    if text_input.canceled:
        return

    lab_name = text_input.get_text()

    # Get lab part(s)
    parts = []

    section_selector = ZybookSectionSelector(allow_optional_and_hidden=True)
    section_numbers = section_selector.select_zybook_sections(
        return_just_numbers=True)

    for chapter, section in section_numbers:
        part = {}
        response = zy_api.get_zybook_section(chapter, section)
        if not response.success:
            popup = ui.layers.Popup("Error", ["Invalid URL"])
            window.run_layer(popup)
        part["name"] = response.name
        part["id"] = response.id
        parts.append(part)

    new_lab = data.model.Lab(lab_name, parts, {})

    edit_lab_options(new_lab)

    all_labs = data.get_labs()
    all_labs.append(new_lab)

    data.write_labs(all_labs)
Example #7
0
def pick_submission(submission_popup: ui.layers.OptionsPopup,
                    lab: data.model.Lab, student: data.model.Student,
                    submission: data.model.Submission):
    """Allow the user to pick a submission to view"""
    window = ui.get_window()
    zy_api = Zybooks()

    # If the lab has multiple parts, prompt to pick a part
    part_index = 0
    if len(lab.parts) > 1:
        part_index = submission.pick_part(pick_all=True)
        if part_index is None:
            return
        if part_index == -1:

            def wait_fn():
                for i, part in enumerate(lab.parts):
                    part_submissions = zy_api.get_submissions_list(
                        part["id"], student.id)
                    if len(part_submissions) > 0:
                        part_response = zy_api.download_assignment_part(
                            lab, student.id, part,
                            len(part_submissions) - 1)
                        submission.update_part(part_response,
                                               lab.parts.index(part))
                set_submission_message(submission_popup, submission)

            popup = ui.layers.WaitPopup("Downloading")
            popup.set_message([f"Downloading latest submissions..."])
            popup.set_wait_fn(wait_fn)
            window.run_layer(popup)
            return

    # Get list of all submissions for that part
    part = lab.parts[part_index]
    all_submissions = zy_api.get_submissions_list(part["id"], student.id)
    if not all_submissions:
        popup = ui.layers.Popup("No Submissions",
                                ["The student did not submit this part"])
        window.run_layer(popup)
        return

    # Reverse to display most recent submission first
    all_submissions.reverse()

    popup = ui.layers.ListLayer("Select Submission", popup=True)
    popup.set_exit_text("Cancel")
    for sub in all_submissions:
        popup.add_row_text(sub)
    window.run_layer(popup)
    if popup.canceled:
        return

    submission_index = popup.selected_index()

    # Modify submission index to un-reverse the index
    submission_index = abs(submission_index - (len(all_submissions) - 1))

    # Fetch that submission
    part_response = zy_api.download_assignment_part(lab, student.id, part,
                                                    submission_index)
    submission.update_part(part_response, lab.parts.index(part))
    set_submission_message(submission_popup, submission)
Example #8
0
 def __init__(self, allow_optional_and_hidden=False):
     self.window = ui.get_window()
     self.zy_api = Zybooks()
     self.allow_optional_and_hidden = allow_optional_and_hidden
Example #9
0
class ZybookSectionSelector:
    def __init__(self, allow_optional_and_hidden=False):
        self.window = ui.get_window()
        self.zy_api = Zybooks()
        self.allow_optional_and_hidden = allow_optional_and_hidden

    def is_allowed(self, section):
        return self.allow_optional_and_hidden or (not (section["hidden"]
                                                       or section["optional"]))

    class _SectionToggle(ui.layers.Toggle):
        def __init__(self, index, data):
            super().__init__()
            self.__index = index
            self.__data = data
            self.get()

        def get(self):
            self._toggled = self.__data[self.__index]

        def toggle(self):
            self.__data[self.__index] = not self.__data[self.__index]
            self.get()

    def select_zybook_sections(self,
                               return_just_numbers=False,
                               title_extra=""):
        self.zybooks_toc = self.zy_api.get_table_of_contents()
        if not self.zybooks_toc:
            return None
        self.zybooks_sections = {(chapter["number"], section["number"]):
                                 section
                                 for chapter in self.zybooks_toc
                                 for section in chapter["sections"]}

        selected_sections = {(chapter["number"], section["number"]): False
                             for chapter in self.zybooks_toc
                             for section in chapter["sections"]}

        title = ("Select zyBooks Sections"
                 if not title_extra else f"{title_extra} - Select Sections")
        chapter_pad_width = len(str(len(self.zybooks_toc)))
        section_pad_width = max([
            len(str(len(chapter["sections"]))) for chapter in self.zybooks_toc
        ])
        popup = ui.layers.ListLayer(title, popup=True)
        popup.set_exit_text("Done")
        for i, chapter in enumerate(self.zybooks_toc, 1):
            row = popup.add_row_parent(
                f"{str(chapter['number']):>{chapter_pad_width}} - {chapter['title']}"
            )
            for j, section in enumerate(chapter["sections"], 1):
                section_string = (f"{chapter['number']}"
                                  f".{section['number']:<{section_pad_width}}"
                                  f" - {section['title']}")
                if not self.is_allowed(section):
                    section_string += " (Optional)"

                subrow = row.add_row_toggle(
                    section_string,
                    ZybookSectionSelector._SectionToggle((i, j),
                                                         selected_sections))

                # Disable selection of optional sections
                if not self.is_allowed(section):
                    subrow.set_disabled()

        self.window.run_layer(popup)

        res = []
        for section_numbers, selected in selected_sections.items():
            if selected:
                if return_just_numbers:
                    res.append(section_numbers)
                else:
                    res.append(self.zybooks_sections[section_numbers])
        return res