Exemple #1
0
    def add_tutorial_package(self):
        """
        Prompt the user to add a tutorial package.

        If there is no default tutorial package, then this tutorial package
        will be added as the default.  Otherwise, it will be added as an
        ordinary package.

        If the package was added as the default package, it will be selected
        automatically.

        """
        # if we don't have a default tutorial, we should add this one as the
        # default and then switch to it
        as_default = not self.cfg.tutorials.default
        msg = add_tutorial(self.cfg, as_default=as_default, window=self.master)

        if msg is not None:
            tkmessagebox.showerror(
                'Failed to Add Tutorial',
                'Could not add tutorial: {}'.format(msg),
            )
            return

        if as_default:
            self.tutorial_package = self.cfg.tutorials.default

        self.menu.set_tutorial_packages(self.cfg.tutorials.names)
Exemple #2
0
    def tutorial_package(self, package_name):
        """
        Select the tutorial package with the given name.

        If no name is provided, prompt the user to add a new tutorial.

        If a name is provided but the package cannot be loaded, display an
        error to the user but otherwise do nothing.
        This may leave the application in an inconsistent state.

        Warning: if no name is provided, prompting the user to add a new
        tutorial is performed *asynchronously*.  Because of this, if a value
        given to this setter could be null, the calling code *cannot* rely on
        tutorial_package having a valid value immediately after this function
        returns.  For example, the following code is *not safe*:

            self.tutorial_package = 'CSSE1001Tutorials' if rand() < 0.5 else ''
            print(self.tutorial_package.name)  # UNSAFE

        If the assigned value is '' in the above example, self.tutorial_package
        will not have a value until, at the earliest, *after* the next run of
        the GUI event loop (as that is when the user will be prompted to add
        a tutorial).

        In normal use, this should not pose a problem; the only time the
        assigned value should even potentially be the empty string is the very
        first time that MPT is run.

        Args:
          package_name (str): The name of the package to select.

        """
        if not package_name:
            # if no package is given, we want to try to add a new one
            # this will be the case the very first time MyPyTutor is launched
            # in that case, the Tk object (root) will not yet have entered
            # the main loop
            # attempting to display any tk widget before entering the main
            # loop will cause the application to suffer display errors on OS X
            # once it actually appears (see issue #49)
            # by deferring the add action, we can avoid this issue
            self.master.after(0, self.add_tutorial_package)
            return

        assert hasattr(self.cfg, package_name), \
                'Attempt to select unknown package: {}'.format(package_name)
        options = getattr(self.cfg, package_name)

        try:
            self._tutorial_package = TutorialPackage(package_name, options)
        except TutorialPackageError as e:
            tkmessagebox.showerror(
                'Invalid Tutorial Package',
                'Failed to load {}: {}'.format(package_name, e),
            )
            return

        # update menu
        self.menu.set_selected_tutorial_package(self.tutorial_package)
Exemple #3
0
    def _display_web_api_error(self, ex):
        """
        Display the given WebAPIError to the user.

        Args:
          ex (WebAPIError): The error to display.

        """
        message = '{}\n\nDetailed error message:\n{}'.format(
            ex.message, ex.details)

        tkmessagebox.showerror('Online Services Error', message)
Exemple #4
0
    def ok(self, event=None):
        user = self.user_entry.get().strip()
        password = self.pass_entry.get()  # don't strip, for obvious reasons

        success = self.callback(user, password)
        if success:
            self.destroy()
        else:
            tkmessagebox.showerror(
                'Login Error',
                'Incorrect username or password',
            )
            self.pass_entry.delete(0, tk.END)
Exemple #5
0
 def ok(self, e=None):
     tut_dir = self.tut_entry.get()
     ans_dir = self.ans_entry.get()
     name = self.name_entry.get()
     if not (os.path.exists(tut_dir)
             and os.path.exists(os.path.join(tut_dir, 'tutorials.txt'))
             and os.path.exists(os.path.join(tut_dir, 'config.txt'))):
         tkmessagebox.showerror('Tutorial Configuration Error',
                                'Invalid tutorial folder.')
     elif ans_dir == '':
         tkmessagebox.showerror('Tutorial Configuration Error',
                                'Invalid answers folder.')
     else:
         self.result = tut_dir, self.ans_entry.get(), name
         self.withdraw()
         self.update_idletasks()
         self.cancel()
Exemple #6
0
    def ok(self, event=None):
        values = {
            'problem_name': self.subject_var.get(),
            'type': self.feedback_type,
            'code_text': self.code,
            'feedback': self.text.get(1.0, tk.END)
        }
        try:
            data = urllib.parse.urlencode(values)
            req = urllib.request.Request(URL, data)
            response = urllib.request.urlopen(req)
            the_page = response.read()
            if 'Feedback not accepted' in the_page:
                tkmessagebox.showerror('Feedback Error',
                                       'Feedback not accepted')
        except:
            tkmessagebox.showerror('Feedback Error', 'Cannot upload feedback')

        super().ok(event=event)
Exemple #7
0
    def remove_current_tutorial_package(self):
        """
        Remove the current tutorial package.

        It is not possible to remove the default tutorial package, or the only
        tutorial package which is available.

        The user will be prompted to confirm the package's removal.  If they
        choose to proceed, the default tutorial package will be selected.

        This process does not delete any problem or answer files.

        """
        if self.cfg.tutorials.default == self.tutorial_package.name:
            tkmessagebox.showerror(
                'Remove Current Tutorial Error',
                'You cannot remove the default tutorial.  ' \
                'Try setting a new default first.'
            )
            return

        if len(self.cfg.tutorials.names) == 1:
            tkmessagebox.showerror(
                'Remove Current Tutorial Error'
                'You cannot remove the last tutorial.  ' \
                'Try adding a new tutorial first.'
            )
            return

        should_remove = tkmessagebox.askquestion(
            'Remove Tutorial?', 'Do you really want to remove {}?'.format(
                self.tutorial_package.name))

        if str(should_remove) == tkmessagebox.YES:
            del self.cfg[self.tutorial_package.name]
            self.cfg.tutorials.names.remove(self.tutorial_package.name)

            self.tutorial_package = self.cfg.tutorials.default

            self.menu.set_tutorial_packages(self.cfg.tutorials.names)
Exemple #8
0
    def login(self):
        """
        Attempt to log the student in to the MyPyTutor system.

        If the login fails, show the student an error message.

        If the login succeeds, set the online status appropriately.

        Returns:
          Whether the login attempt succeeded.

        """
        if not self.web_api.login():
            tkmessagebox.showerror(
                'Login failed',
                'Please check your credentials and try again.  ' \
                'If the problem persists, check your internet connection.  ' \
                'Some functionality (such as submitting answers) will be ' \
                'unavailable until you log in successfully.'
            )
            return False

        self._set_online_status(logged_in_user=self.web_api.user)
        return True
Exemple #9
0
 def _show_failure_message():
     tkmessagebox.showerror(
         'Could Not Synchronise Answer Code',
         'Please check that you are correctly logged in, ' \
         'and that your internet connection is active.'
     )
Exemple #10
0
    def login(self):
        """Log in to the MyPyTutor server.

        If the credentials are valid, a cookie will be set automatically.
        If the credentials are invalid, prompt the user to retry.
        If the user cancels the login attempt, raise an AuthError.

        Precondition: the form is the standard auth.uq.edu.au login form.
        """
        # Ask for the user's information.
        data = {'action': 'userinfo'}
        url = self._url + '?' + urllib.parse.urlencode(data)
        response = self._opener.open(url)
        text = response.read().decode('utf8')

        def set_details(text):
            # Get the user's details out of the server's response.
            self._user = json.loads(strip_header(text))
            self._callback()

        # If we didn't get redirected to the login form, we're already in.
        if urllib.parse.urlsplit(response.geturl()).netloc != LOGIN_DOMAIN:
            set_details(text)
            tkmessagebox.showerror(
                'Login Error',
                "You are already logged in as {}.".format(self._user['user'])
            )
            return

        # Construct a callback for the login dialog box.
        def submit_login(username, password):
            """Given a username and password, attempt to submit the login
            form. Return True if it succeeded.
            """
            # Extract the login form.
            action, form_data = FormParser.get_auth_form(text)

            # Put the user's credentials into the form
            form_data['username'] = username
            form_data['password'] = password

            # Submit the form.
            form_url = urllib.parse.urljoin(response.geturl(), action)
            form_data = urllib.parse.urlencode(form_data).encode('ascii')
            response2 = self._opener.open(form_url, form_data)

            # Get the HTML text in the response.
            text2 = response2.read().decode('utf8')

            # If there is a login form in the response, the user's credentials are invalid.
            if any(f[0].get('name') == 'f' for f in FormParser.forms(text2)):
                return False

            # There is one more form to automatically submit.
            action, form_data = FormParser.get_consume_form(text2)
            form_url = urllib.parse.urljoin(response2.geturl(), action)
            form_data = urllib.parse.urlencode(form_data).encode('ascii')

            # The next response should contain the information originally requested.
            response3 = self._opener.open(form_url, form_data)
            text3 = response3.read().decode('utf8')
            set_details(text3)

            # The login was successful
            return True

        LoginDialog(None, submit_login)
        if not self.is_logged_in():
            raise AuthError("Not logged in.")