예제 #1
0
def load_default_config(ipython_dir=None):
    """Load the default config file from the default ipython_dir.

    This is useful for embedded shells.
    """
    if ipython_dir is None:
        ipython_dir = get_ipython_dir()

    profile_dir = os.path.join(ipython_dir, 'profile_default')

    config = Config()
    for cf in Application._load_config_files("ipython_config", path=profile_dir):
        config.update(cf)

    return config
예제 #2
0
def load_default_config(ipython_dir=None):
    """Load the default config file from the default ipython_dir.

    This is useful for embedded shells.
    """
    if ipython_dir is None:
        ipython_dir = get_ipython_dir()

    profile_dir = os.path.join(ipython_dir, 'profile_default')

    config = Config()
    for cf in Application._load_config_files("ipython_config",
                                             path=profile_dir):
        config.update(cf)

    return config
예제 #3
0
파일: course.py 프로젝트: hsmohammed/rudaux
    def __init__(self, course_dir=None, auto=False) -> 'Course':
        """Initialize a course from a config file. 
    :param course_dir: The directory your course. If none, defaults to current working directory. 
    :type course_dir: str
    :param auto: Suppress all prompts, automatically answering yes.
    :type auto: bool

    :returns: A Course object for performing operations on an entire course at once.
    :rtype: Course
    """

        #=======================================#
        #     Working Directory & Git Sync      #
        #=======================================#

        # Set up the working directory. If no course_dir has been specified, then it
        # is assumed that this is the course directory.
        self.working_directory = course_dir if course_dir is not None else os.getcwd(
        )

        repo = Repo(self.working_directory)

        # Before we do ANYTHING, make sure our working directory is clean with no
        # untracked files! Unless we're running a automated job, in which case we
        # don't want to fail for an unexpected reason.
        if (repo.is_dirty() or repo.untracked_files) and (not auto):
            continue_with_dirty = input("""
        Your repository is currently in a dirty state (modifications or
        untracked changes are present). We strongly suggest that you resolve
        these before proceeding. Continue? [y/n]:""")
            # if they didn't say no, exit
            if continue_with_dirty.lower() != 'y':
                sys.exit("Exiting...")

        # PRINT BANNER
        print(
            AsciiTable([['Initializing Course and Pulling Instructors Repo']
                        ]).table)

        # pull the latest copy of the repo
        utils.pull_repo(repo_dir=self.working_directory)

        # Make sure we're running our nbgrader commands within our instructors repo.
        # this will contain our gradebook database, our source directory, and other
        # things.
        config = Config()
        config.CourseDirectory.root = self.working_directory

        #=======================================#
        #              Load Config              #
        #=======================================#

        # Check for an nbgrader config file...
        if not os.path.exists(
                os.path.join(self.working_directory, 'nbgrader_config.py')):
            # if there isn't one, make sure there's at least a rudaux config file
            if not os.path.exists(
                    os.path.join(self.working_directory, 'rudaux_config.py')):
                sys.exit("""
          You do not have nbgrader_config.py or rudaux_config.py in your current
          directory. We need at least one of these to set up your course
          parameters! You can specify a directory with the course_dir argument
          if you wish.
          """)

        # use the traitlets Application class directly to load nbgrader config file.
        # reference:
        # https://github.com/jupyter/nbgrader/blob/41f52873c690af716c796a6003d861e493d45fea/nbgrader/server_extensions/validate_assignment/handlers.py#L35-L37

        # ._load_config_files() returns a generator, so if the config is missing,
        # the generator will act similarly to an empty array

        # load rudaux_config if it exists, otherwise just bring in nbgrader_config.
        for rudaux_config in Application._load_config_files(
                'rudaux_config', path=self.working_directory):
            config.merge(rudaux_config)

        for nbgrader_config in Application._load_config_files(
                'nbgrader_config', path=self.working_directory):
            config.merge(nbgrader_config)

        #=======================================#
        #           Set Config Params           #
        #=======================================#

        ## NBGRADER PARAMS

        # If the user set the exchange, perform home user expansion if necessary
        if config.get('Exchange', {}).get('root') is not None:
            # perform home user expansion. Should not throw an error, but may
            try:
                # expand home user in-place
                config['Exchange']['root'] = os.path.expanduser(
                    config['Exchange']['root'])
            except:
                pass

        ## CANVAS PARAMS

        # Before we continue, make sure we have all of the necessary parameters.
        self.course_id = config.get('Canvas', {}).get('course_id')
        self.canvas_url = config.get('Canvas', {}).get('canvas_url')
        self.external_tool_name = config.get('Canvas',
                                             {}).get('external_tool_name')
        self.external_tool_level = config.get('Canvas',
                                              {}).get('external_tool_level')
        # The canvas url should have no trailing slash
        self.canvas_url = re.sub(r"/$", "", self.canvas_url)

        ## GITHUB PARAMS
        self.stu_repo_url = config.get('GitHub', {}).get('stu_repo_url', '')
        self.assignment_release_path = config.get(
            'GitHub', {}).get('assignment_release_path')
        self.ins_repo_url = config.get('GitHub', {}).get('ins_repo_url')
        # subpath not currently supported
        # self.ins_dir_subpath = config.get('GitHub').get('ins_dir_subpath')

        ## JUPYTERHUB PARAMS

        self.hub_url = config.get('JupyterHub', {}).get('hub_url')
        # The hub url should have no trailing slash
        self.hub_url = re.sub(r"/$", "", self.hub_url)
        # Get Storage directory & type
        self.storage_path = config.get('JupyterHub', {}).get('storage_path', )
        self.zfs = config.get('JupyterHub',
                              {}).get('zfs')  # Optional, default is false!
        self.zfs_regex = config.get('JupyterHub',
                                    {}).get('zfs_regex')  # default is false!
        self.zfs_datetime_pattern = config.get('JupyterHub', {}).get(
            'zfs_datetime_pattern')  # default is false!
        # Note hub_prefix, not base_url, to avoid any ambiguity
        self.hub_prefix = config.get('JupyterHub', {}).get('base_url')
        # If prefix was set, make sure it has no trailing slash, but a preceding
        # slash
        if self.hub_prefix is not None:
            self.hub_prefix = re.sub(r"/$", "", self.hub_prefix)
            if re.search(r"^/", self.hub_prefix) is None:
                self.hub_prefix = fr"/{self.hub_prefix}"

        ## COURSE PARAMS

        self.grading_image = config.get('Course', {}).get('grading_image')
        self.tmp_dir = config.get('Course', {}).get('tmp_dir')
        assignment_list = config.get('Course', {}).get('assignments')

        self.course_timezone = config.get('Course', {}).get('timezone')
        self.system_timezone = pendulum.now(tz='local').timezone.name

        ## Repurpose the rest of the params for later batches
        ## (Hang onto them in case we need something)

        self._full_config = config

        #=======================================#
        #        Validate URLs (Slightly)       #
        #=======================================#

        urls = {
            'JupyterHub.hub_url': self.hub_url,
            'Canvas.canvas_url': self.canvas_url
        }

        for key, value in urls.items():
            if re.search(r"^https{0,1}", value) is None:
                sys.exit(f"""
          You must specify the scheme (e.g. https://) for all URLs.
          You are missing the scheme in "{key}":
          {value}
          """)
            if re.search(r".git$", value) is not None:
                sys.exit(f"""
          Please do not use .git-appended URLs. 
          You have used a .git url in "{key}":
          {value}
          """)

        #=======================================#
        #       Check For Required Params       #
        #=======================================#

        # Finally, before we continue, make sure all of our required parameters were
        # specified in the config file(s)
        required_params = {
            "Canvas.course_id": self.course_id,
            "Canvas.canvas_url": self.canvas_url,
            "GitHub.stu_repo_url": self.stu_repo_url,
            "GitHub.ins_repo_url": self.ins_repo_url,
            "JupyterHub.hub_url": self.hub_url,
            "Course.assignments": assignment_list
        }

        # If any are none...
        if None in required_params.values():
            # Figure out which ones are none and let the user know.
            for key, value in required_params.items():
                if value is None:
                    print(f"    \"{key}\" is missing.")
            sys.exit(
                'Please make sure you have specified all required parameters in your config file.'
            )

        #=======================================#
        #       Check For Optional Params       #
        #=======================================#

        # Now look for all of our optional parameters. If any are missing, let the
        # user know we'll be using the default.
        optional_params = {
            "assignment_release_path": {
                "value": self.assignment_release_path,
                "default": 'materials',
                "config_name": "GitHub.assignment_release_path"
            },
            # "assignment_source_path": {
            #   "value": self.assignment_source_path,
            #   "default": "source",
            #   "config_name": "c.GitHub.assignment_source_path"
            # },
            "hub_prefix": {
                "value": self.hub_prefix,
                "default": "",
                "config_name": "JupyterHub.base_url"
            },
            "zfs": {
                "value": self.zfs,
                "default": False,
                "config_name": "JupyterHub.zfs"
            },
            "zfs_regex": {
                "value": self.zfs_regex,
                "default": r'\d{4}-\d{2}-\d{2}-\d{4}',
                "config_name": "JupyterHub.zfs_regex"
            },
            "zfs_datetime_pattern": {
                "value": self.zfs_datetime_pattern,
                "default": 'YYYY-MM-DD-HHmm',
                "config_name": "JupyterHub.zfs_datetime_pattern"
            },
            "course_timezone": {
                "value": self.course_timezone,
                "default": 'US/Pacific',
                "config_name": "Course.timezone"
            },
            "grading_image": {
                "value": self.grading_image,
                "default": 'ubcdsci/r-dsci-grading',
                "config_name": "Course.grading_image"
            },
            "tmp_dir": {
                "value": self.tmp_dir,
                "default": os.path.join(Path.home(), 'tmp'),
                "config_name": "Course.tmp_dir"
            },
            "external_tool_name": {
                "value": self.external_tool_name,
                "default": 'Jupyter',
                "config_name": "Canvas.external_tool_name"
            },
            "external_tool_level": {
                "value": self.external_tool_level,
                "default": 'course',
                "config_name": "Canvas.external_tool_level"
            }
        }

        for key, param in optional_params.items():
            if param.get('value') is None:
                setattr(self, key, param.get('default'))
                print(
                    f"    \"{param.get('config_name')}\" is missing, using default parameter of \"{getattr(self, key)}\""
                )

        # Make sure no preceding or trailing slashes in assignment release path
        self.assignment_release_path = re.sub(r"/$", "",
                                              self.assignment_release_path)
        self.assignment_release_path = re.sub(r"^/", "",
                                              self.assignment_release_path)

        # Since we are using the student repo URL for the Launch URLs
        # (i.e. telling nbgitpuller where to find the notebook),
        # if the user provided an SSH url, we need the https version as well.
        self.stu_launch_url = utils.generate_git_urls(
            self.stu_repo_url).get('plain_https')

        #! this is cheating a bit, but we can get the repo name this way
        #! Fix me in the future
        self.ins_repo_name = os.path.split(
            utils.generate_git_urls(self.ins_repo_url).get('plain_https'))[1]
        self.stu_repo_name = os.path.split(self.stu_launch_url)[1]

        #=======================================#
        #           Set Canvas Token            #
        #=======================================#

        canvas_token_name = config.get('Canvas').get('token_name')

        if canvas_token_name is None:
            print("Searching for default Canvas token, CANVAS_TOKEN...")
            canvas_token_name = 'CANVAS_TOKEN'

        self.canvas_token = self._get_token(canvas_token_name)

        #=======================================#
        #        Finalize Setting Params        #
        #=======================================#

        # set up the nbgrader api with our merged config files
        self.nb_api = NbGraderAPI(config=config)

        # assign init params to object
        # self.canvas_token = self._get_token(canvas_token_name)
        # self.course = self._get_course()

        # Set crontab
        # self.cron = CronTab(user=True)
        # We need to use the system crontab because we'll be making ZFS snapshots
        # which requires elevated permissions
        self.cron = CronTab(user=True)

        #=======================================#
        #        Instantiate Assignments        #
        #=======================================#

        # Subclass assignment for this course:
        class CourseAssignment(rudaux.Assignment):
            course = self

        instantiated_assignments = []

        for _assignment in assignment_list:
            assignment = CourseAssignment(
                name=_assignment.get('name'),
                duedate=_assignment.get('duedate'),
                duetime=_assignment.get(
                    'duetime', '23:59:59'),  # default is 1 sec to midnight
                points=_assignment.get('points', 0),  # default is zero points
                manual=_assignment.get('manual',
                                       False),  # default is no manual grading
            )
            instantiated_assignments.append(assignment)

        self.assignments = instantiated_assignments