예제 #1
0
def synchronise_problems(web_api):
    """
    Synchronise problems for the default tutorial package.

    Args:
      web_api (WebAPI): The WebAPI instance to use.  This must be logged in.

    """
    from tutorlib.config.configuration import load_config
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.online.sync import SyncClient

    print('Synchronising tutorial problems...', end='', flush=True)

    cfg = load_config()
    try:
        options = getattr(cfg, cfg.tutorials.default)
    except AttributeError:
        print('failed')
        return

    try:
        tutorial_package = TutorialPackage(cfg.tutorials.default, options)
    except TutorialPackageError:
        print('failed')
        return

    client = SyncClient(web_api)
    if not client.synchronise(tutorial_package):
        print('failed')
        return

    print('done')
예제 #2
0
def synchronise_problems(web_api):
    """
    Synchronise problems for the default tutorial package.

    Args:
      web_api (WebAPI): The WebAPI instance to use.  This must be logged in.

    """
    from tutorlib.config.configuration import load_config
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.online.sync import SyncClient

    print('Synchronising tutorial problems...', end='', flush=True)

    cfg = load_config()
    try:
        options = getattr(cfg, cfg.tutorials.default)
    except AttributeError:
        print('failed')
        return

    try:
        tutorial_package = TutorialPackage(cfg.tutorials.default, options)
    except TutorialPackageError:
        print('failed')
        return

    client = SyncClient(web_api)
    if not client.synchronise(tutorial_package):
        print('failed')
        return

    print('done')
예제 #3
0
def try_get_credentials():
    """
    Try to get the user's credentials.

    If the keyring module is not installed, prompt the user to install it.
    If the user refuses, set a flag so as not to prompt again.

    If the module is installed and the user has saved credentials, return them.
    Otherwise, prompt the user to enter credentials to save.

    Returns:
      A tuple containing the user's username and password.

    """
    # we need access to the config file
    from tutorlib.config.configuration import load_config, save_config
    cfg = load_config()

    # return if the user has said not to store anything
    if not cfg.online.store_credentials:
        return None, None

    try:
        import keyring
    except ImportError:
        print()
        print('We can securely store your username and password using the '
              'keyring module')
        store = input(
            "Type 'yes' if you would like us to store your credentials: ")
        if store == 'yes':
            install = input(
                "Type 'yes' if you would like to install the keyring module: ")

        # if the user said no, remember their choice
        # we also remember if the install failed, as this is unlikely to change
        if store != 'yes' or install != 'yes' or not install_keyring_module():
            cfg.online.store_credentials = False
            save_config(cfg)

            return None, None

        import keyring  # should cause no problems at this point

    # if we have a username, return the associated password
    if cfg.online.username:
        password = keyring.get_password(MPT_SERVICE, cfg.online.username)
        return cfg.online.username, password

    # grab the user's username and password
    print()
    print('Please enter your UQ username and password')
    cfg.online.username = input('Username: ')
    password = getpass()
    print()

    save_config(cfg)
    keyring.set_password(MPT_SERVICE, cfg.online.username, password)

    return cfg.online.username, password
예제 #4
0
    def reset_credentials():
        import keyring
        from tutorlib.config.configuration import load_config, save_config
        cfg = load_config()

        try:
            keyring.delete_password(MPT_SERVICE, cfg.online.username)
        except Exception:  # PasswordDeleteError isn't defined anywhere!
            pass

        cfg.online.username = ''

        save_config(cfg)
예제 #5
0
    def reset_credentials():
        import keyring
        from tutorlib.config.configuration import load_config, save_config
        cfg = load_config()

        try:
            keyring.delete_password(MPT_SERVICE, cfg.online.username)
        except Exception:  # PasswordDeleteError isn't defined anywhere!
            pass

        cfg.online.username = ''

        save_config(cfg)
예제 #6
0
def try_get_credentials():
    """
    Try to get the user's credentials.

    If the keyring module is not installed, prompt the user to install it.
    If the user refuses, set a flag so as not to prompt again.

    If the module is installed and the user has saved credentials, return them.
    Otherwise, prompt the user to enter credentials to save.

    Returns:
      A tuple containing the user's username and password.

    """
    # we need access to the config file
    from tutorlib.config.configuration import load_config, save_config
    cfg = load_config()

    # return if the user has said not to store anything
    if not cfg.online.store_credentials:
        return None, None

    try:
        import keyring
    except ImportError:
        print()
        print(
            'We can securely store your username and password using the '
            'keyring module'
        )
        store = input(
            "Type 'yes' if you would like us to store your credentials: "
        )
        if store == 'yes':
            install = input(
                "Type 'yes' if you would like to install the keyring module: "
            )

        # if the user said no, remember their choice
        # we also remember if the install failed, as this is unlikely to change
        if store != 'yes' or install != 'yes' or not install_keyring_module():
            cfg.online.store_credentials = False
            save_config(cfg)

            return None, None

        import keyring  # should cause no problems at this point

    # if we have a username, return the associated password
    if cfg.online.username:
        password = keyring.get_password(MPT_SERVICE, cfg.online.username)
        return cfg.online.username, password

    # grab the user's username and password
    print()
    print('Please enter your UQ username and password')
    cfg.online.username = input('Username: ')
    password = getpass()
    print()

    save_config(cfg)
    keyring.set_password(MPT_SERVICE, cfg.online.username, password)

    return cfg.online.username, password
예제 #7
0
def update_default_tutorial_package(force_update=False):
    """
    Update the default tutorial package if necessary.

    Args:
      force_update (bool, optional): If True, update regardless of whether a
        newer version of the tutorial package is available on the server.

    """
    from tutorlib.config.configuration import load_config
    from tutorlib.gui.app.support \
            import remove_directory_contents, safely_extract_zipfile
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.interface.web_api import WebAPI, WebAPIError

    print('Checking for tutorial package updates...', end='', flush=True)

    # grab our config file
    cfg = load_config()
    package_name = cfg.tutorials.default
    package_options = getattr(cfg, package_name)

    # try to open the tutorial package
    try:
        tutorial_package = TutorialPackage(package_name, package_options)
    except TutorialPackageError:
        print('failed')
        return

    # check if we need to do an update at all
    web_api = WebAPI()

    try:
        timestamp = web_api.get_tutorials_timestamp()
    except WebAPIError:
        print('failed')
        return

    # we need to be comparing as ints
    create_tuple = lambda t: tuple(map(int, t.split('.')))
    server_timestamp = create_tuple(timestamp)
    local_timestamp = create_tuple(tutorial_package.timestamp)

    print('done')

    # we only want to update if the server's version is more recent
    # a more recent local version should only arise in development, anyway
    if server_timestamp <= local_timestamp and not force_update:
        return

    print('Updating tutorial package...', end='', flush=True)

    # grab the zipfile
    try:
        zip_path = web_api.get_tutorials_zipfile()
    except WebAPIError:
        print('failed')
        return

    # extract the zipfile into our empty tutorial directory
    remove_directory_contents(tutorial_package.options.tut_dir)
    safely_extract_zipfile(zip_path, tutorial_package.options.tut_dir)

    print('done')
예제 #8
0
def bootstrap_tutorials():
    """
    If the default tutorial path does not exist, download and extract the
    tutorials zipfile.

    If no default tutorial package is specified, CSSE1001Tutorials will be
    assumed (this is the default package name used by this script).

    """
    from tutorlib.config.configuration import load_config, save_config
    from tutorlib.gui.app.support import safely_extract_zipfile
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.interface.web_api import WebAPI, WebAPIError

    # grab our config file
    cfg = load_config()
    options = getattr(cfg, cfg.tutorials.default or 'CSSE1001Tutorials')

    def tutorials_are_installed():
        if not options.tut_dir:  # no entry in config at all (default)
            return False
        if not os.path.exists(options.tut_dir):  # no package directory at all
            return False
        if not os.path.exists(options.ans_dir):  # no answers dir
            return False

        try:
            _ = TutorialPackage(cfg.tutorials.default, options)
        except TutorialPackageError:
            return False

        return True

    if not tutorials_are_installed():
        print('Downloading default tutorial package...', end='', flush=True)

        web_api = WebAPI()
        try:
            filename = web_api.get_tutorials_zipfile()
        except WebAPIError:
            print('failed')
            sys.exit(1)

        print('done')

        # set the default tutorial directory
        # our default tutorial directory is in the same directory as the script
        # note that this assumes we used the default config, which created the
        # CSSE1001Tutorials key
        print('Installing default tutorial package...', end='', flush=True)

        script_dir = get_script_dir()
        options.tut_dir = os.path.join(script_dir, 'CSSE1001Tutorials')
        options.ans_dir = os.path.join(script_dir, 'CSSE1001Answers')

        safely_extract_zipfile(filename, options.tut_dir)

        if not os.path.exists(options.ans_dir):
            os.mkdir(options.ans_dir)

        save_config(cfg)

        print('done')
예제 #9
0
    def __init__(self, master, web_api=None):
        assert web_api is None or web_api.is_logged_in, \
                'If a WebAPI instance is provided, it must be logged in'

        #### Set up the window
        master.title('MyPyTutor')
        master.protocol("WM_DELETE_WINDOW", self.close)

        #### Set up our menu
        self.menu = TutorialMenu(master, delegate=self)
        master.config(menu=self.menu)

        #### Set up local variables
        ## Optionals / property bases
        self.current_tutorial = None
        self._editor = None
        self._tutorial_package = None
        self._submissions = {}

        ## Important top-level vars
        self.master = master
        self.cfg = load_config()

        ## Vars with side effects
        self.tutorial_package = self.cfg.tutorials.default
        self.menu.set_tutorial_packages(self.cfg.tutorials.names)

        ## Objects
        self.attempts = TutorialAttempts()
        self.interpreter = Interpreter()

        if web_api is None:
            self.web_api = WebAPI(self._login_status_change)
            self.master.after(0, self.login)
        else:
            self.web_api = web_api
            self.web_api.listener = self._login_status_change

            # immediately perform the callback, assuming we've synced already
            self._login_status_change(logged_in=True, do_sync=False)

        self.sync_client = SyncClient(self.web_api)

        ## Purely private vars
        self._is_closing = False

        ## Finalise GUI Setup

        width = min(master.winfo_screenwidth(), self.cfg.resolution.width)
        height = min(master.winfo_screenheight(), self.cfg.resolution.height)
        master.geometry('{}x{}'.format(width, height))

        #### Create GUI Widgets
        ## Top Frame
        top_frame = ttk.Frame(master)
        top_frame.pack(fill=tk.BOTH, expand=tk.TRUE)

        ## Tutorial (html display of tutorial problem)
        self.tutorial_frame = TutorialFrame(
            top_frame,
        )
        self.tutorial_frame.pack(fill=tk.BOTH, expand=tk.TRUE)
        self.tutorial_frame.splash(version=VERSION)

        ## Short Problem Description
        self.short_description = ttk.Label(top_frame)  # TODO: sort out style
        self.short_description.pack()

        ## Toolbar (hints, login status etc)
        toolbar = ttk.Frame(top_frame)  # TODO: sort out style
        toolbar.pack(side=tk.TOP, fill=tk.X)

        self.hint_button = ttk.Button(
            toolbar, text='Next Hint', command=self._next_hint
        )

        self.online_status = ttk.Label(
            toolbar, relief=tk.SUNKEN
        )
        self.online_status.pack(
            side=tk.RIGHT, pady=3, ipady=2, padx=2, ipadx=2
        )
        self._set_online_status(logged_in_user=None)

        self.problem_status = ttk.Label(
            toolbar, relief=tk.SUNKEN
        )
        self.problem_status.pack(
            side=tk.RIGHT, pady=3, ipady=2, padx=2, ipadx=2
        )

        ## Test Output
        self.test_output = TestOutput(
            top_frame,
            self,
        )
        self.test_output.pack(fill=tk.X, expand=tk.FALSE)

        ## Analysis Output
        self.analysis_output = AnalysisOutput(
            top_frame,
        )
        self.analysis_output.pack(fill=tk.X, expand=tk.FALSE)
예제 #10
0
파일: app.py 프로젝트: dartmoth/MyPyTutor
    def __init__(self, master):
        #### Set up the window
        master.title('MyPyTutor')
        master.protocol("WM_DELETE_WINDOW", self.close)

        #### Set up our menu
        self.menu = TutorialMenu(master, delegate=self)
        master.config(menu=self.menu)

        #### Set up local variables
        ## Optionals / property bases
        self.current_tutorial = None
        self._editor = None
        self._tutorial_package = None

        ## Important top-level vars
        self.master = master
        self.cfg = load_config()

        ## Vars with side effects
        self.tutorial_package = self.cfg.tutorials.default
        self.menu.set_tutorial_packages(self.cfg.tutorials.names)

        ## Objects
        self.web_api = WebAPI()
        master.after(0, self.synchronise)  # post event immediately after init

        #### Create GUI Widgets
        ## Top Frame
        top_frame = ttk.Frame(master)
        top_frame.pack(fill=tk.BOTH, expand=tk.TRUE)

        ## Tutorial (html display of tutorial problem)
        self.tutorial_frame = TutorialFrame(
            top_frame, (self.cfg.font.name, self.cfg.font.size),
            self.cfg.window_sizes.problem)
        self.tutorial_frame.pack(fill=tk.BOTH, expand=tk.TRUE)
        self.tutorial_frame.splash(version=VERSION)

        ## Short Problem Description
        self.short_description = ttk.Label(top_frame)  # TODO: sort out style
        self.short_description.pack()

        ## Toolbar (hints, login status etc)
        toolbar = ttk.Frame(top_frame)  # TODO: sort out style
        toolbar.pack(side=tk.TOP, fill=tk.X)

        self.hint_button = ttk.Button(toolbar,
                                      text='Next Hint',
                                      command=self._next_hint)

        self.online_status = ttk.Label(toolbar, relief=tk.SUNKEN)
        self.online_status.pack(side=tk.RIGHT,
                                pady=3,
                                ipady=2,
                                padx=2,
                                ipadx=2)
        self._set_online_status(logged_in_user=None)

        ## Test Output
        self.test_output = TestOutput(
            top_frame,
            self.cfg.font.size,
            self.cfg.window_sizes.output,
        )
        self.test_output.pack(fill=tk.BOTH, expand=0)

        ## Analysis Output
        self.analysis_output = AnalysisOutput(
            top_frame,
            self.cfg.font.size,
            self.cfg.window_sizes.analysis,
        )
        self.analysis_output.pack(fill=tk.BOTH, expand=0)
예제 #11
0
def update_default_tutorial_package(force_update=False):
    """
    Update the default tutorial package if necessary.

    Args:
      force_update (bool, optional): If True, update regardless of whether a
        newer version of the tutorial package is available on the server.

    """
    from tutorlib.config.configuration import load_config
    from tutorlib.gui.app.support \
            import remove_directory_contents, safely_extract_zipfile
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.interface.web_api import WebAPI, WebAPIError

    print('Checking for tutorial package updates...', end='', flush=True)

    # grab our config file
    cfg = load_config()
    package_name = cfg.tutorials.default
    package_options = getattr(cfg, package_name)

    # try to open the tutorial package
    try:
        tutorial_package = TutorialPackage(package_name, package_options)
    except TutorialPackageError:
        print('failed')
        return

    # check if we need to do an update at all
    web_api = WebAPI()

    try:
        timestamp = web_api.get_tutorials_timestamp()
    except WebAPIError:
        print('failed')
        return

    # we need to be comparing as ints
    create_tuple = lambda t: tuple(map(int, t.split('.')))
    server_timestamp = create_tuple(timestamp)
    local_timestamp = create_tuple(tutorial_package.timestamp)

    print('done')

    # we only want to update if the server's version is more recent
    # a more recent local version should only arise in development, anyway
    if server_timestamp <= local_timestamp and not force_update:
        return

    print('Updating tutorial package...', end='', flush=True)

    # grab the zipfile
    try:
        zip_path = web_api.get_tutorials_zipfile()
    except WebAPIError:
        print('failed')
        return

    # extract the zipfile into our empty tutorial directory
    remove_directory_contents(tutorial_package.options.tut_dir)
    safely_extract_zipfile(zip_path, tutorial_package.options.tut_dir)

    print('done')
예제 #12
0
def bootstrap_tutorials():
    """
    If the default tutorial path does not exist, download and extract the
    tutorials zipfile.

    If no default tutorial package is specified, CSSE1001Tutorials will be
    assumed (this is the default package name used by this script).

    """
    from tutorlib.config.configuration import load_config, save_config
    from tutorlib.gui.app.support import safely_extract_zipfile
    from tutorlib.interface.problems \
            import TutorialPackage, TutorialPackageError
    from tutorlib.interface.web_api import WebAPI, WebAPIError

    # grab our config file
    cfg = load_config()
    options = getattr(cfg, cfg.tutorials.default or 'CSSE1001Tutorials')

    def tutorials_are_installed():
        if not options.tut_dir:  # no entry in config at all (default)
            return False
        if not os.path.exists(options.tut_dir):  # no package directory at all
            return False
        if not os.path.exists(options.ans_dir):  # no answers dir
            return False

        try:
            _ = TutorialPackage(cfg.tutorials.default, options)
        except TutorialPackageError:
            return False

        return True

    if not tutorials_are_installed():
        print('Downloading default tutorial package...', end='', flush=True)

        web_api = WebAPI()
        try:
            filename = web_api.get_tutorials_zipfile()
        except WebAPIError:
            print('failed')
            sys.exit(1)

        print('done')

        # set the default tutorial directory
        # our default tutorial directory is in the same directory as the script
        # note that this assumes we used the default config, which created the
        # CSSE1001Tutorials key
        print('Installing default tutorial package...', end='', flush=True)

        script_dir = get_script_dir()
        options.tut_dir = os.path.join(script_dir, 'CSSE1001Tutorials')
        options.ans_dir = os.path.join(script_dir, 'CSSE1001Answers')

        safely_extract_zipfile(filename, options.tut_dir)

        if not os.path.exists(options.ans_dir):
            os.mkdir(options.ans_dir)

        save_config(cfg)

        print('done')