Esempio n. 1
0
    def questions(self) -> List[QuestionBase]:
        """Access the list of questions.

        :return: the list of questions within the multi-part assessment
        :rtype: list(:py:class:`~regions.tutor.assessment.QuestionBase`)

        :raises :py:class:`~utils.tutor.TutorException`: if a task step doesn't
            match a free response or multiple choice question or if no
            assessment segments are found

        """
        parts = []
        for question in self.find_elements(*self._question_locator):
            if question.find_elements(*self._is_free_response_locator):
                parts.append(FreeResponse(self, question))
            elif question.find_elements(*self._is_multiple_choice_locator):
                parts.append(MultipleChoice(self, question))
            else:
                tag = question.get_attribute('data-task-step-id')
                raise TutorException(
                    f'Unknown assessment type in task step {tag}')
        if not parts:
            raise TutorException('No multi-part steps found in "{0}"'.format(
                self.driver.current_url))
        return parts
Esempio n. 2
0
    def select_assignment(self, name: str, _type: str = None):
        """Click on an assignment.

        :param str name: the assignment's name
        :param str _type: (optional) the assignment's type
        :return: the assignment task page(s)
        :rtype: :py:class:`~pages.tutor.task.Assignment`

        """
        for assignment in self.find_elements(*self._assignment_link_locator):
            if name in assignment.get_attribute('textContent'):
                description = assignment.get_attribute('class')
                if _type and _type not in description:
                    continue
                if Tutor.EVENT in description:
                    from pages.tutor.task import Event as Destination
                elif Tutor.EXTERNAL in description:
                    from pages.tutor.task import EXTERNAL as Destination
                elif Tutor.HOMEWORK in description:
                    from pages.tutor.task import Homework as Destination
                elif Tutor.READING in description:
                    from pages.tutor.task import Reading as Destination
                else:
                    raise TutorException(
                        f'Unknown assignment type in "{description}"')
                Utility.click_option(self.driver, element=assignment)
                sleep(1)
                return go_to_(Destination(self.driver, self.base_url))
        raise TutorException(f'"{name}" not found in the currently ' +
                             'available assignments')
Esempio n. 3
0
        def get_instructor(self,
                           first_name=None,
                           last_name=None,
                           position=None):
            """Return the instructor row based on their name or position.

            :param str first_name: (optional) match the instructor row by the
                first match on the teacher's first name
            :param str last_name: (optional) match the instructor row by the
                first match on the teacher's last name
            :param int position: (optional) select the instructor row by the
                row position (e.g. ``position = 1`` is the first instructor)
            :return: the instructor row
            :rtype: :py:class:`~CourseRoster.Teachers.Teacher`

            :raises TutorException: if a match isn't found

            """
            if position:
                return self.instructors[position - 1]
            elif first_name:
                for instructor in self.instructors:
                    if instructor.first_name == first_name:
                        return instructor
                raise TutorException(
                    'No instructor matches first name "{0}"'.format(
                        first_name))
            elif last_name:
                for instructor in self.instructors:
                    if instructor.last_name == last_name:
                        return instructor
                raise TutorException(
                    'No instructor matches last name "{0}"'.format(last_name))
            raise TutorException('No match option provided')
Esempio n. 4
0
 def student_access(self):
     """Access the student payment information."""
     try:
         root = self.find_element(*self._student_payment_locator)
         return self.Payment(self, root)
     except WebDriverException:
         raise TutorException('No active trial')
Esempio n. 5
0
    def purchase(self) -> Union[PurchaseConfirmation]:
        """Click on the 'Purchase' Tutor button.

        :return: the purchase confirmation modal or the error message for a
            failed transaction
        :rtype: :py:class:`~pages.tutor.enrollment.PurchaseConfirmation`
        :raises: :py:class:`~utils.tutor.TutorException` if error messages are
            found

        """
        purchase = self.find_element(*self._base_iframe_locator)
        self.driver.switch_to.frame(purchase)
        button = self.wait.until(
            expect.presence_of_element_located(self._purchase_button_locator))
        sleep(0.25)
        is_not_chrome = not Utility.is_browser(self.driver, 'chrome')
        Utility.click_option(self.driver,
                             element=button,
                             force_js_click=is_not_chrome)
        sleep(1)
        self.driver.switch_to.default_content()
        errors = self.error_messages
        if errors:
            raise TutorException(errors)
        return PurchaseConfirmation(self.page)
Esempio n. 6
0
    def go_to_course(self, name: str):
        """Go to a specific course.

        :param str name: the course name to select
        :return: the calendar (teacher) or current week (student) page for the
            specific course
        :rtype: :py:class:`~pages.tutor.calendar.Calendar` or
            :py:class:`~pages.tutor.course.StudentCourse`

        :raises :py:class:`~utils.tutor.TutorException`: if no previous course
            or current course exists or if a course does not match ``name``

        """
        if self.is_safari:
            sleep(2)
            self.wait.until(lambda _: bool(self.current_courses.courses))
        # Look through current courses first
        try:
            for course in self.current_courses.courses:
                if course.title == name:
                    return course.go_to_course()
        except AssertionError:
            pass

        # Then try previous courses
        try:
            for course in self.past_courses.courses:
                if course.title == name:
                    return course.go_to_course()
        except AssertionError:
            pass

        raise TutorException(f'No course found matching "{name}"')
Esempio n. 7
0
        def _add_assignment(self, assignment_type):
            """Click on an add assignment button.

            An internal helper function to select a new assignment.

            :param str assignment_type: the type of assignment to add
            :return: an assignment creation page
            :rtype: :py:class:`~pages.tutor.assignment.AddExternal` or
                :py:class:`~pages.tutor.assignment.AddEvent` or
                :py:class:`~pages.tutor.assignment.AddHomework` or
                :py:class:`~pages.tutor.assignment.AddReading`
            :raises :py:class:`~utils.tutor.TutorException`: if the
                assignment_type does not match a known assignment type

            :noindex:

            """
            if assignment_type == Tutor.EXTERNAL:
                locator = self._add_external_locator
                from pages.tutor.assignment import External as Assignment
            elif assignment_type == Tutor.EVENT:
                locator = self._add_event_locator
                from pages.tutor.assignment import Event as Assignment
            elif assignment_type == Tutor.HOMEWORK:
                locator = self._add_homework_locator
                from pages.tutor.assignment import Homework as Assignment
            elif assignment_type == Tutor.READING:
                locator = self._add_reading_locator
                from pages.tutor.assignment import Reading as Assignment
            else:
                raise TutorException('"{0}" is not a known assignment type.'
                                     .format(assignment_type))
            button = self.find_element(*locator)
            Utility.click_option(self.driver, element=button)
            return go_to_(Assignment(self.driver, self.page.base_url))
Esempio n. 8
0
 def chat_with_support(self):
     """Begin a customer support chat session."""
     if self.chat_available:
         from pages.tutor.chat import Chat
         self._select(locator=self._chat_support_enabled_locator,
                      destination=Chat,
                      external=True)
     raise TutorException('Chat is not currently available')
Esempio n. 9
0
 def performance(self):
     """Access the performance bars for homeworks and readings."""
     if self.page.assignment_type == Tutor.EXTERNAL:
         return TutorException(
             'External assignments do not have performance bars.')
     return [
         self.Performance(self, bar)
         for bar in self.find_elements(*self._performance_locator)
     ]
Esempio n. 10
0
        def students(self, students: int = 1) -> None:
            """Set the estimated number of students who will enroll.

            :param int students: the estimated number of students who will
                enroll in the course
            :return: None

            :raises :py:class:`~utils.tutor.TutorException`: if the number of
                students is less than 1 or greater than 1,500

            """
            if students < 1:
                raise TutorException(
                    f'the minimum student estimate is 1 ({students} < 1)')
            self.find_element(*self._student_estimate_locator) \
                .send_keys(students)
            sleep(0.25)
            error = self.error
            if error:
                raise TutorException(error.strip())
Esempio n. 11
0
        def sections(self, sections: int = 1) -> None:
            """Set the initial number of course sections or periods to create.

            :param int sections: the requested number of sections to create for
                the new course
            :return: None

            :raises :py:class:`~utils.tutor.TutorException`: if the number of
                sections is less than 1 or greater than 10

            """
            if sections < 1:
                raise TutorException(
                    f'the minimum number of sections is 1 ({sections} < 1)')
            section_box = self.find_element(*self._course_sections_locator)
            self.driver.execute_script('arguments[0].value = "";', section_box)
            section_box.send_keys(sections)
            sleep(0.25)
            error = self.error
            if error:
                raise TutorException(error.strip())
Esempio n. 12
0
    def get_course_tile(self, name: str) -> Courses.Course:
        """Return a course tile by matching the course name.

        :param str name: the course name to match
        :return: the course picker course tile
        :rtype: :py:class:`~pages.tutor.dashboard.Courses.Course`

        """
        for course in self.courses:
            if course.title == name:
                return course
        raise TutorException(f'No course found matching "{name}"')
Esempio n. 13
0
        def select_section(self, name=None, position=1):
            """Select a section or period tab by name or position.

            .. note::

               section/period names are case sensitive (e.g. '1st' and '1ST'
               can occur in the same course)

            :param str name: (optional) the section or period name to select
            :param int position: (optional) the position of the section or
                period tab to select from 1 to the number of tabs
            :return: the course roster displaying the selected course section
                roster
            :rtype: :py:class:`CourseRoster`

            :raises :py:class:`~utils.tutor.TutorException`: if the name does
                not match an existing tab name or if the position is not
                between 1 and number of active tabs

            """
            if name:
                section_found = False
                for section in self.sections:
                    if section.name == name:
                        section_found = True
                        section.select()
                if not section_found:
                    raise TutorException(
                        '"{name}" does not match any active section'.format(
                            name=name))
            else:
                maximum = len(self.sections)
                if position < 1 or position > maximum:
                    raise TutorException(
                        "position {pos} is not between 1 and {max}".format(
                            position, maximum))
                self.sections[position - 1].select()
            sleep(0.5)
            return self.page
Esempio n. 14
0
    def random_answer(self) -> None:
        """Select a random answer for the question.

        :return: None
        :raises: :py:class:`~utils.tutor.TutorException` if no multiple choice
            answers are available

        """
        sleep(0.25)
        answers = self.answers
        if not answers:
            raise TutorException('No answers found')
        answers[Utility.random(0, len(answers) - 1)].select()
        sleep(0.25)
Esempio n. 15
0
        def sections(self):
            """Access the section forecasts.

            :return: the list of sections in the recent forecast
            :rtype: list(:py:class:`~StudentCourse.Performance.Section`)

            :raises :py:class:`~utils.tutor.TutorException`: if the recent
                topics forecast is empty

            """
            if self.is_empty:
                raise TutorException("Forecast is not populated")
            return [self.Section(self, section)
                    for section in self.find_elements(*self._section_locator)]
Esempio n. 16
0
        def select_by_name(self, name: str) -> None:
            """Select a course to clone by name.

            :param str name: the existing course name
            :return: None

            :raises :py:class:`~utils.tutor.TutorException`: if the name is
                not found or is not available

            """
            for option in self.courses:
                if option.name.lower() == name.lower():
                    option.select()
                    return
            raise TutorException(f'"{name}" not found or not available')
Esempio n. 17
0
    def forecast(self):
        """Access the performance forecast rows.

        :return: the forecast panels
        :rtype: :py:class:`~PerformanceForecast.Guide`

        :raise :py:class:`~utils.tutor.TutorException`: if the guide is not
            found

        """
        try:
            forecast = self.find_element(*self._group_forecast_locator)
            return self.Guide(self, forecast)
        except NoSuchElementException:
            raise TutorException('Performance guide not found; is there data?')
Esempio n. 18
0
    def view_section(self, name):
        """View a course section by its name.

        :return: the performance forecast for the requested section or period
        :rtype: :py:class:`PerformanceForecast`

        :raises :py:class:`utils.tutor.TutorException`: if the name doesn't
            match an available course section

        """
        for tab in self.section_tabs:
            if tab.name == name:
                tab.select()
                return self.page
        raise TutorException('"{0}" does not match any active section')
Esempio n. 19
0
        def select_by_title(self, title: str) -> None:
            """Select a course book by the book title.

            :param str title: the book title
            :return: None

            :raises :py:class:`~utils.tutor.TutorException`: if the title is
                not found or is not available

            """
            for option in self.books:
                if option.title.lower() == title.lower():
                    option.select()
                    return
            raise TutorException(f'"{title}" not found or not available')
Esempio n. 20
0
        def select_by_term(self, term: str) -> None:
            """Select a term by the semester or quarter name.

            :param str term: the term to select
            :return: None

            :raises :py:class:`~utils.tutor.TutorException`: if the term is not
                found or is not available

            """
            for option in self.terms:
                if option.term.lower() == term.lower():
                    option.select()
                    return
            raise TutorException(f'"{term}" not found or not available')
Esempio n. 21
0
    def assignment(self, name: str) -> Assignment:
        """Return the assignment from its name.

        :param str name: the assignment name or title
        :return: the assignment
        :rtype: :py:class:`~pages.tutor.calendar.Assignment`

        :raises :py:class:`~utils.tutor.TutorException` if an assignment does
            not match ``name``

        """
        for assignment in self.assignments():
            if assignment.title == name:
                return assignment
        raise TutorException(f'No assignment found matching "{name}"')
Esempio n. 22
0
            def add_assignment(self, assignment_type=None):
                """Click on the date number to add an assignment.

                :param str assignment_type: the type of assignment to add
                :return: an assignment creation page
                :rtype: :py:class:`~pages.tutor.assignment.AddExternal` or
                        :py:class:`~pages.tutor.assignment.AddEvent` or
                        :py:class:`~pages.tutor.assignment.AddHomework` or
                        :py:class:`~pages.tutor.assignment.AddReading`

                :raises :py:class:`~utils.tutor.TutorException`: if the
                        assignment_type does not match a known assignment type

                """
                label = self.find_element(*self._date_label_locator)
                Utility.click_option(self.driver, element=label)
                sleep(0.5)
                if assignment_type == Tutor.EVENT:
                    assignment = Tutor.EVENT
                    from pages.tutor.assignment import Event \
                        as Assignment
                elif assignment_type == Tutor.EXTERNAL:
                    assignment = Tutor.EXTERNAL
                    from pages.tutor.assignment import External \
                        as Assignment
                elif assignment_type == Tutor.HOMEWORK:
                    assignment = Tutor.HOMEWORK
                    from pages.tutor.assignment import Homework \
                        as Assignment
                elif assignment_type == Tutor.READING:
                    assignment = Tutor.READING
                    from pages.tutor.assignment import Reading \
                        as Assignment
                elif assignment_type is None:
                    # just open the pop up menu
                    return self.page.page
                else:
                    raise TutorException(
                        '"{0}" is not a known assignment type.'
                        .format(assignment_type))
                if assignment:
                    button = self.driver.execute_script(
                        'return document.querySelector("{}");'.format(
                            self._add_assignment_selector.format(assignment)))
                    Utility.click_option(self.driver, element=button)
                    return go_to_(Assignment(self.driver,
                                             self.page.page.base_url))
Esempio n. 23
0
    def select_timezone(self, timezone=Tutor.CENTRAL_TIME):
        """Select a timezone from the available radio options.

        :param str timezone: (optional) the new timezone, defaults to
            :py:data:`~utils.tutor.Tutor.CENTRAL_TIME`
        :return: the timezone modal
        :rtype: :py:class:`ChangeCourseTimezone`

        :raise :py:class:`utils.tutor.TutorException`: if the timezone does not
            match an available option

        """
        for zone in self.timezones:
            if zone.name == timezone:
                zone.select()
                return self
        raise TutorException('"{0}" is not a known timezone'.format(timezone))
Esempio n. 24
0
    def answer(self) -> Practice:
        """Submit the answer.

        :return: the next step in the practice session
        :rtype: :py:class:`~pages.tutor.practice.Practice`

        :raises :py:class:`~utils.tutor.TutorException`: if the answer button
            is disabled

        """
        if not self.answer_enabled:
            raise TutorException("The answer button is currently disabled; "
                                 "next step not available")
        button = self.find_element(*self._answer_button_locator)
        Utility.click_option(self.driver, element=button)
        sleep(1)
        return self
Esempio n. 25
0
            def term(self):
                """Return whether the date is in or out of the term.

                :return: the status of a date with respect to the course term
                :rtype: str

                """
                term = self.root.get_attribute('class')
                if Tutor.BEFORE_TERM in term:
                    return Tutor.BEFORE_TERM
                elif Tutor.IN_TERM in term:
                    return Tutor.IN_TERM
                elif Tutor.AFTER_TERM in term:
                    return Tutor.AFTER_TERM
                else:
                    raise TutorException('No term-status listed in "{0}"'
                                         .format(term.split()))
Esempio n. 26
0
    def delete(self):
        """Click on the 'Delete' button confirming the section removal.

        :return: the course roster
        :rtype: :py:class:`CourseRoster`
        :raises: :py:class:`~utils.tutor.TutorException` when the course
            period/section cannot be deleted

        """
        button = self.find_element(*self._delete_button_locator)
        Utility.click_option(self.driver, element=button)
        sleep(0.25)
        try:
            self.wait.until(expect.staleness_of(self.root))
        except TimeoutException:
            raise TutorException('Could not delete the course section')
        sleep(1)
        return self.page
Esempio n. 27
0
    def name(self, name):
        """Change the course name.

        :param str name: the course's new name or title
        :return: the course rename modal
        :rtype: :py:class:`RenameCourse`
        :raises :py:class:`~utils.tutor.TutorException`: if name is not a valid
            course name (``bool(name) == False``)

        """
        if not name:
            raise TutorException('"{0}" is not a valid course name'
                                 .format(name))
        field = self.find_element(*self._course_name_input_locator)
        Utility.clear_field(self.driver, field=field)
        sleep(0.25)
        field.send_keys(name)
        sleep(0.75)
        return self
Esempio n. 28
0
    def create_a_course(self):
        """Select the create course tile.

        :return: the course creation wizard
        :rtype: :py:class:`~pages.tutor.new_course.NewCourse`

        """
        try:
            self.wait.until(
                lambda _: self.find_elements(*self._create_tile_locator))
        except TimeoutException:
            raise TutorException("Create a course tile not found; "
                                 "check user's faculty verification")
        tile = self.find_elements(*self._create_tile_locator)
        link = tile[0].find_element(By.CSS_SELECTOR, 'a')
        Utility.scroll_to(self.driver, element=tile[0], shift=-80)
        Utility.click_option(self.driver, element=link)
        from pages.tutor.new_course import NewCourse
        return go_to_(NewCourse(self.driver, self.base_url))
Esempio n. 29
0
    def state(self, state: str) -> None:
        """Set the district, state, or territory.

        :param str state: the state's select menu label for the district,
            state, or U.S. territory
        :return: None

        :raises :py:class:`~utils.tutor.TutorException`: if the state is not a
            valid state label

        """
        if state not in Tutor.states():
            raise TutorException(
                '"{0}" not a valid state; refer to '.format(state) +
                'utils.tutor.States for valid options')
        purchase = self.find_element(*self._base_iframe_locator)
        self.driver.switch_to.frame(purchase)
        Utility.select(self.driver, self._state_locator, state)
        self.driver.switch_to.default_content()
Esempio n. 30
0
    def assignment_bar(self, name: str, _type: str = None):
        """Return the assignment bar for an assignment.

        :param str name: the assignment's name
        :param str _type: (optional) the assignment's type
        :return: the assignment status bar
        :rtype: :py:class:`~pages.tutor.task.AssignmentBar`

        """
        sleep(0.5)
        assignments = [AssignmentBar(self, bar)
                       for bar
                       in self.find_elements(*self._assignment_bar_locator)]
        for assignment in assignments:
            if assignment.title == name:
                if _type and assignment.style != _type:
                    continue
                return assignment
        raise TutorException(f'"{name}" not found in the currently ' +
                             'available assignments')