Exemplo n.º 1
0
def initialize_class(class_name):
    try:
        config = GraderConfiguration(single_class_name=class_name)
    except ConfigurationError as e:
        sys.exit('Config error: {0}'.format(e))

    if class_name not in config.students_by_class:
        sys.exit('No student CSV file for {0}'.format(class_name))

    try:
        home_dir = home_dir_from_username(config.username, config.username,
                                          config.host)
    except CommandError as e:
        sys.exit('Error getting remote home dir for {0}:\n{1}'
                 .format(config.username, e))

    class_path = os.path.join(home_dir, class_name)

    if directory_exists(class_path, config.username, config.host):
        sys.exit('{0} already exists'.format(class_path))

    print('Initializing class', class_name, 'on', config.host)

    assignments_path = os.path.join(class_path, 'assignments')

    for dir_path in [class_path, assignments_path]:
        if directory_exists(dir_path, config.username, config.host):
            sys.exit('{0} already exists'.format(dir_path))
        try:
            create_directory(dir_path, config.username, config.host)
        except CommandError as e:
            sys.exit('Error creating {0}: {1}'.format(dir_path, e))

        print('Created', dir_path)

    try:
        remote_home_dir = home_dir_from_username(config.username,
                                                 config.username,
                                                 config.host)
        remote_config_dir = os.path.join(remote_home_dir, '.config/grader')

        scp_file(config.students_csv_filenames_by_class[class_name],
                 config.username, config.host, remote_config_dir)
    except CommandError as e:
        sys.exit('Error copying student file:\n{0}'.format(e))

    print('Copied', config.students_csv_filenames_by_class[class_name], 'to',
          class_path, 'on', config.host)

    print('Class', class_name, 'initialized on', config.host)
    print()
    print('Next, run populate_students.py on', config.host)
def initialize_class(class_name):
    try:
        config = GraderConfiguration(single_class_name=class_name)
    except ConfigurationError as e:
        sys.exit('Config error: {0}'.format(e))

    if class_name not in config.students_by_class:
        sys.exit('No student CSV file for {0}'.format(class_name))

    try:
        home_dir = home_dir_from_username(config.username, config.username,
                                          config.host)
    except CommandError as e:
        sys.exit('Error getting remote home dir for {0}:\n{1}'.format(
            config.username, e))

    class_path = os.path.join(home_dir, class_name)

    if directory_exists(class_path, config.username, config.host):
        sys.exit('{0} already exists'.format(class_path))

    print('Initializing class', class_name, 'on', config.host)

    assignments_path = os.path.join(class_path, 'assignments')

    for dir_path in [class_path, assignments_path]:
        if directory_exists(dir_path, config.username, config.host):
            sys.exit('{0} already exists'.format(dir_path))
        try:
            create_directory(dir_path, config.username, config.host)
        except CommandError as e:
            sys.exit('Error creating {0}: {1}'.format(dir_path, e))

        print('Created', dir_path)

    try:
        remote_home_dir = home_dir_from_username(config.username,
                                                 config.username, config.host)
        remote_config_dir = os.path.join(remote_home_dir, '.config/grader')

        scp_file(config.students_csv_filenames_by_class[class_name],
                 config.username, config.host, remote_config_dir)
    except CommandError as e:
        sys.exit('Error copying student file:\n{0}'.format(e))

    print('Copied', config.students_csv_filenames_by_class[class_name], 'to',
          class_path, 'on', config.host)

    print('Class', class_name, 'initialized on', config.host)
    print()
    print('Next, run populate_students.py on', config.host)
Exemplo n.º 3
0
def get_log_path_from_username(username: str) -> str:
    """Creates the path to the log file for the given student or faculty
    username.

    :param username: the user who owns the log
    :return: the path to the log
    """
    
    filename = 'git-keeper-{0}.log'.format(username)
    home_dir = home_dir_from_username(username)

    log_path = os.path.join(home_dir, filename)

    return log_path
Exemplo n.º 4
0
def get_log_path_from_username(username: str) -> str:
    """Creates the path to the log file for the given student or faculty
    username.

    :param username: the user who owns the log
    :return: the path to the log
    """

    filename = 'git-keeper-{0}.log'.format(username)
    home_dir = home_dir_from_username(username)

    log_path = os.path.join(home_dir, filename)

    return log_path
Exemplo n.º 5
0
    def __init__(self, last_name, first_name, email_address, ssh=None):
        self.first_name = first_name
        self.last_name = last_name
        self.email_address = email_address
        self.ssh = ssh

        split_email = self.email_address.split('@')

        if len(split_email) != 2:
            error = '{0} does not appear to be a valid email address'\
                .format(email_address)
            raise StudentException(error)

        self.username = split_email[0]

        self.home_dir = home_dir_from_username(self.username, ssh=self.ssh)
Exemplo n.º 6
0
    def __init__(self, last_name, first_name, email_address, ssh=None):
        self.first_name = first_name
        self.last_name = last_name
        self.email_address = email_address
        self.ssh = ssh

        split_email = self.email_address.split('@')

        if len(split_email) != 2:
            error = '{0} does not appear to be a valid email address'\
                .format(email_address)
            raise StudentException(error)

        self.username = split_email[0]

        self.home_dir = home_dir_from_username(self.username, ssh=self.ssh)
Exemplo n.º 7
0
def populate_students(class_name):

    try:
        config = GraderConfiguration(on_grading_server=True)
    except ConfigurationError as e:
        sys.exit('Config error: {0}'.format(e))

    home_dir = home_dir_from_username(config.username)

    grader_class_path = os.path.join(home_dir, class_name)

    if not directory_exists(grader_class_path):
        sys.exit(
            '{0} does not exist.\nClass appears to be uninitialized.'.format(
                grader_class_path))

    print('Creating student directories for class', class_name)

    students = config.students_by_class[class_name]

    if len(students) == 0:
        sys.exit('No students found for class {0}'.format(class_name))

    create_users = False
    for student in students:
        assert isinstance(student, Student)
        if not user_exists(student.username):
            if not create_users:
                print('User', student.username, 'does not exist')
                if y_or_n('Create nonexistent users? (y/n) '):
                    create_users = True
            if create_users:
                try:
                    create_user(student.username)
                    home_dir = home_dir_from_username(student.username)
                    chown(home_dir, student.username, config.group)
                    chmod(home_dir, '770', sudo=True)
                    print('Created user', student.username)
                except CommandError as e:
                    sys.exit('Error creating user {0}:\n{1}'.format(
                        student.username, e))
            else:
                sys.exit('Not creating users, exiting')

        home_dir = home_dir_from_username(student.username)

        student_class_path = os.path.join(home_dir, class_name)

        if directory_exists(student_class_path):
            print('{0} already exists, skipping'.format(student_class_path))
            continue

        try:
            create_directory(student_class_path)
            chmod(student_class_path, '775')
            print('Created', student_class_path)
        except OSError as e:
            error = 'Error creating {0}: {1}'.format(student_class_path, e)
            sys.exit(error)

    print('Student directories created successfully')
    print('You may now start the grader daemon')
Exemplo n.º 8
0
def update_assignment_tests(class_name, local_assignment_dir):

    local_assignment_dir = os.path.expanduser(local_assignment_dir)
    local_assignment_dir = os.path.abspath(local_assignment_dir)

    assignment = os.path.basename(local_assignment_dir.rstrip('/'))

    test_code_dir = os.path.join(local_assignment_dir, 'tests')

    if not os.path.isdir(test_code_dir):
        sys.exit('{0} does not exist'.format(test_code_dir))

    action_file_path = os.path.join(test_code_dir, 'action.sh')

    if not os.path.isfile(action_file_path):
        sys.exit('No action.sh in {0}'.format(test_code_dir))

    try:
        config = GraderConfiguration(single_class_name=class_name)
    except ConfigurationError as e:
        sys.exit(e)

    try:
        ssh = SSHClient()
        ssh.load_system_host_keys()
        ssh.connect(config.host, username=config.username)
    except OSError as e:
        sys.exit('Error opening SSH connection:\n{0}'.format(e))

    if class_name not in config.students_by_class:
        sys.exit('Class {0} does not exist'.format(class_name))

    test_code_repo_tempdir = TemporaryDirectory()

    try:
        test_code_repo = copy_and_create_repo(test_code_dir,
                                              test_code_repo_tempdir.name,
                                              assignment)
    except CommandError as e:
        error = 'Error copying test code repo:\n{0}'.format(e)
        sys.exit(error)

    try:
        remote_home_dir = home_dir_from_username(config.username, ssh=ssh)

    except CommandError as e:
        sys.exit('Error getting remote home dir for {0}:\n{1}'.format(
            config.username, e))

    remote_assignment_dir = os.path.join(remote_home_dir, class_name,
                                         'assignments', assignment)

    tests_bare_repo_dir = os.path.join(remote_assignment_dir,
                                       '{0}_tests.git'.format(assignment))

    if not directory_exists(tests_bare_repo_dir, ssh=ssh):
        sys.exit('{0} does not exist on {1}'.format(tests_bare_repo_dir,
                                                    config.host))

    print('Updating test repo for assignment', assignment)

    tests_bare_repo = Repository(tests_bare_repo_dir,
                                 assignment,
                                 is_local=False,
                                 is_bare=True,
                                 remote_host=config.host,
                                 remote_user=config.username)

    try:
        test_code_repo.push(tests_bare_repo, force=True)
        print('Pushed tests to', tests_bare_repo_dir)
    except CommandError as e:
        sys.exit('Error pushing test repo:\n{0}'.format(e))

    print(assignment, 'tests updated successfully')
Exemplo n.º 9
0
def populate_students(class_name):

    try:
        config = GraderConfiguration(on_grading_server=True)
    except ConfigurationError as e:
        sys.exit('Config error: {0}'.format(e))

    home_dir = home_dir_from_username(config.username)

    grader_class_path = os.path.join(home_dir, class_name)

    if not directory_exists(grader_class_path):
        sys.exit('{0} does not exist.\nClass appears to be uninitialized.'
                 .format(grader_class_path))

    print('Creating student directories for class', class_name)

    students = config.students_by_class[class_name]

    if len(students) == 0:
        sys.exit('No students found for class {0}'.format(class_name))

    create_users = False
    for student in students:
        assert isinstance(student, Student)
        if not user_exists(student.username):
            if not create_users:
                print('User', student.username, 'does not exist')
                if y_or_n('Create nonexistent users? (y/n) '):
                    create_users = True
            if create_users:
                try:
                    create_user(student.username)
                    home_dir = home_dir_from_username(student.username)
                    chown(home_dir, student.username, config.group)
                    chmod(home_dir, '770', sudo=True)
                    print('Created user', student.username)
                except CommandError as e:
                    sys.exit('Error creating user {0}:\n{1}'
                             .format(student.username, e))
            else:
                sys.exit('Not creating users, exiting')

        home_dir = home_dir_from_username(student.username)

        student_class_path = os.path.join(home_dir, class_name)

        if directory_exists(student_class_path):
            print('{0} already exists, skipping'.format(student_class_path))
            continue

        try:
            create_directory(student_class_path)
            chmod(student_class_path, '775')
            print('Created', student_class_path)
        except OSError as e:
            error = 'Error creating {0}: {1}'.format(student_class_path, e)
            sys.exit(error)

    print('Student directories created successfully')
    print('You may now start the grader daemon')
Exemplo n.º 10
0
def update_assignment_tests(class_name, local_assignment_dir):

    local_assignment_dir = os.path.expanduser(local_assignment_dir)
    local_assignment_dir = os.path.abspath(local_assignment_dir)

    assignment = os.path.basename(local_assignment_dir.rstrip('/'))

    test_code_dir = os.path.join(local_assignment_dir, 'tests')

    if not os.path.isdir(test_code_dir):
        sys.exit('{0} does not exist'.format(test_code_dir))

    action_file_path = os.path.join(test_code_dir, 'action.sh')

    if not os.path.isfile(action_file_path):
        sys.exit('No action.sh in {0}'.format(test_code_dir))

    try:
        config = GraderConfiguration(single_class_name=class_name)
    except ConfigurationError as e:
        sys.exit(e)

    try:
        ssh = SSHClient()
        ssh.load_system_host_keys()
        ssh.connect(config.host, username=config.username)
    except OSError as e:
        sys.exit('Error opening SSH connection:\n{0}'.format(e))

    if class_name not in config.students_by_class:
        sys.exit('Class {0} does not exist'.format(class_name))

    test_code_repo_tempdir = TemporaryDirectory()

    try:
        test_code_repo = copy_and_create_repo(test_code_dir,
                                              test_code_repo_tempdir.name,
                                              assignment)
    except CommandError as e:
        error = 'Error copying test code repo:\n{0}'.format(e)
        sys.exit(error)

    try:
        remote_home_dir = home_dir_from_username(config.username, ssh=ssh)

    except CommandError as e:
        sys.exit('Error getting remote home dir for {0}:\n{1}'
                 .format(config.username, e))

    remote_assignment_dir = os.path.join(remote_home_dir, class_name,
                                         'assignments', assignment)

    tests_bare_repo_dir = os.path.join(remote_assignment_dir,
                                       '{0}_tests.git'.format(assignment))

    if not directory_exists(tests_bare_repo_dir, ssh=ssh):
        sys.exit('{0} does not exist on {1}'.format(tests_bare_repo_dir,
                                                    config.host))

    print('Updating test repo for assignment', assignment)

    tests_bare_repo = Repository(tests_bare_repo_dir, assignment,
                                 is_local=False, is_bare=True,
                                 remote_host=config.host,
                                 remote_user=config.username)

    try:
        test_code_repo.push(tests_bare_repo, force=True)
        print('Pushed tests to', tests_bare_repo_dir)
    except CommandError as e:
        sys.exit('Error pushing test repo:\n{0}'.format(e))

    print(assignment, 'tests updated successfully')
Exemplo n.º 11
0
    def __init__(self, single_class_name=None, on_grading_server=False):
        self.on_grading_server = on_grading_server

        self.config_dir = os.path.expanduser('~/.config/grader')

        if not os.path.isdir(self.config_dir):
            error = '{0} does not exist'.format(self.config_dir)
            raise ConfigurationError(error)

        self.config_filename = os.path.join(self.config_dir, 'grader.conf')

        if not os.path.isfile(self.config_filename):
            error = '{0} does not exist'.format(self.config_filename)
            raise ConfigurationError(error)

        parser = configparser.ConfigParser()

        try:
            parser.read(self.config_filename)
        except configparser.MissingSectionHeaderError:
            error = 'Error reading {0}'.format(self.config_filename)
            raise ConfigurationError(error)

        sections = parser.sections()

        sections.sort()

        if sections != ['email', 'server']:
            error = 'Config file requires two sections: server and email'
            raise ConfigurationError(error)

        try:
            self.username = parser.get('server', 'username')
            self.host = parser.get('server', 'host')
            self.group = parser.get('server', 'group')

            self.from_name = parser.get('email', 'from_name')
            self.from_address = parser.get('email', 'from_address')
            if parser.has_option('email', 'smtp_server'):
                self.smtp_server = parser.get('email', 'smtp_server')
            else:
                self.smtp_server = None
            if parser.has_option('email', 'smtp_port'):
                self.smtp_port = parser.get('email', 'smtp_port')
            else:
                self.smtp_port = None
            if parser.has_option('email', 'email_username'):
                self.email_username = parser.get('email', 'email_username')
            else:
                self.email_username = None
            if parser.has_option('email', 'email_password'):
                self.email_password = parser.get('email', 'email_password')
            else:
                self.email_password = None
        except configparser.NoOptionError as e:
            raise ConfigurationError(e.message)

        if self.on_grading_server:
            self.ssh = None
        else:
            try:
                self.ssh = SSHClient()
                self.ssh.load_system_host_keys()
                self.ssh.connect(self.host, username=self.username)
            except OSError as e:
                error = ('Error opening SSH connection to {0}:\n{1}'
                         .format(self.host, e))
                raise ConfigurationError(error)

        try:
            self.home_dir = home_dir_from_username(self.username, ssh=self.ssh)
        except CommandError as e:
            raise ConfigurationError('Error connecting to {0} via SSH:\n{1}'
                                     .format(self.host, e))

        self.students_by_class = {}
        self.students_csv_filenames_by_class = {}
        self.students_by_username = {}

        for filename in os.listdir(self.config_dir):
            if not filename.endswith('.csv'):
                continue

            class_name, _ = os.path.splitext(filename)

            if single_class_name is not None \
                    and class_name != single_class_name:
                continue

            if ' ' in class_name:
                error = 'Error in class name "{0}".\n'.format(class_name)
                error += 'Class names may not contain spaces'
                raise ConfigurationError(error)

            self.students_by_class[class_name] = []

            filepath = os.path.join(self.config_dir, filename)

            self.students_csv_filenames_by_class[class_name] = filepath

            try:
                with open(filepath) as f:
                    rows = list(csv.reader(f))
            except OSError as e:
                error = 'Error opening {0}:\n{1}'.format(filepath, e)
                raise ConfigurationError(error)

            for row in rows:
                try:
                    student_row = row
                    student = Student(*student_row, ssh=self.ssh)
                    self.students_by_class[class_name].append(student)
                    self.students_by_username[student.username] = student
                except TypeError:
                    row_str = ','.join(row)
                    error = 'Error in {0} at this row:\n{1}'.format(filepath,
                                                                    row_str)
                    raise ConfigurationError(error)

        if len(self.students_by_class) == 0:
            raise ConfigurationError('No classes defined')

        for class_name, students in self.students_by_class.items():
            if len(students) == 0:
                error = 'No students in {0}'.format(class_name)
                raise ConfigurationError(error)
def upload_assignment(class_name, grader_project):

    grader_project_path = os.path.dirname(sys.argv[0])
    post_update_path = os.path.join(grader_project_path, 'post-update')

    local_assignment_dir = os.path.expanduser(grader_project)
    local_assignment_dir = os.path.abspath(local_assignment_dir)

    assignment = os.path.basename(local_assignment_dir.rstrip('/'))

    base_code_dir = os.path.join(local_assignment_dir, 'base_code')
    test_code_dir = os.path.join(local_assignment_dir, 'tests')

    if not os.path.isdir(base_code_dir):
        sys.exit('{0} does not exist'.format(base_code_dir))

    if not os.path.isdir(test_code_dir):
        sys.exit('{0} does not exist'.format(test_code_dir))

    action_file_path = os.path.join(test_code_dir, 'action.sh')

    if not os.path.isfile(action_file_path):
        sys.exit('No action.sh in {0}'.format(test_code_dir))

    email_file_path = os.path.join(local_assignment_dir, 'email.txt')

    if not os.path.isfile(email_file_path):
        sys.exit('{0} does not exist'.format(email_file_path))

    try:
        config = GraderConfiguration(single_class_name=class_name)
    except ConfigurationError as e:
        sys.exit(e)

    if class_name not in config.students_by_class:
        sys.exit('Class {0} does not exist'.format(class_name))

    base_code_repo_tempdir = TemporaryDirectory()

    try:
        base_code_repo = copy_and_create_repo(base_code_dir,
                                              base_code_repo_tempdir.name,
                                              assignment, 'created assignment')
    except CommandError as e:
        error = 'Error copying base code repo:\n{0}'.format(e)
        sys.exit(error)

    test_code_repo_tempdir = TemporaryDirectory()

    try:
        test_code_repo = copy_and_create_repo(test_code_dir,
                                              test_code_repo_tempdir.name,
                                              assignment)
    except CommandError as e:
        error = 'Error copying test code repo:\n{0}'.format(e)
        sys.exit(error)

    try:
        remote_home_dir = home_dir_from_username(config.username,
                                                 config.username, config.host)
    except CommandError as e:
        sys.exit('Error getting remote home dir for {0}:\n{1}'.format(
            config.username, e))

    remote_assignment_dir = os.path.join(remote_home_dir, class_name,
                                         'assignments', assignment)

    tests_bare_repo_dir = os.path.join(remote_assignment_dir,
                                       '{0}_tests.git'.format(assignment))
    reports_bare_repo_dir = os.path.join(remote_assignment_dir,
                                         '{0}_reports.git'.format(assignment))

    if directory_exists(tests_bare_repo_dir, config.username, config.host):
        sys.exit('{0} already exists on {1}'.format(tests_bare_repo_dir,
                                                    config.host))
    if directory_exists(reports_bare_repo_dir, config.username, config.host):
        sys.exit('{0} already exists on {1}'.format(reports_bare_repo_dir,
                                                    config.host))

    print('Uploading assignment', assignment)

    reports_repo_tempdir = TemporaryDirectory()
    reports_repo_dir = reports_repo_tempdir.name

    reports_repo = Repository(reports_repo_dir, assignment)
    reports_repo.init()

    for student in config.students_by_class[class_name]:
        assert isinstance(student, Student)
        bare_repo_dir = student.get_bare_repo_dir(class_name, assignment)
        if directory_exists(bare_repo_dir, config.username, config.host):
            sys.exit('{0} already exists on {1}'.format(
                bare_repo_dir, config.host))
        student_repo = Repository(bare_repo_dir,
                                  assignment,
                                  is_local=False,
                                  is_bare=True,
                                  remote_user=config.username,
                                  remote_host=config.host,
                                  student_username=student.username)
        try:
            student_repo.init()
            student_repo.add_update_flag_hook(post_update_path)
            base_code_repo.push(student_repo)
            chmod_world_writable_recursive(student_repo.path,
                                           remote_user=config.username,
                                           remote_host=config.host)
            print('Pushed base code to', bare_repo_dir)
        except CommandError as e:
            sys.exit('Error creating {0} on {1}:\n{2}'.format(
                bare_repo_dir, config.host, e))

        student_report_dir = os.path.join(reports_repo_dir,
                                          student.get_last_first_username())
        os.makedirs(student_report_dir)
        placeholder_path = os.path.join(student_report_dir, '.placeholder')
        touch(placeholder_path)

    reports_bare_repo = Repository(reports_bare_repo_dir,
                                   assignment,
                                   is_local=False,
                                   is_bare=True,
                                   remote_user=config.username,
                                   remote_host=config.host)

    try:
        reports_repo.add_all_and_commit('added student directories')
        reports_bare_repo.init()
        reports_repo.push(reports_bare_repo)
        print('Created reports repository in', reports_bare_repo_dir)
    except CommandError as e:
        sys.exit('Error creating reports repository:\n{0}'.format(e))

    tests_bare_repo = Repository(tests_bare_repo_dir,
                                 assignment,
                                 is_local=False,
                                 is_bare=True,
                                 remote_user=config.username,
                                 remote_host=config.host)

    try:
        tests_bare_repo.init()
        test_code_repo.push(tests_bare_repo)
        print('Pushed tests to', tests_bare_repo_dir)
    except CommandError as e:
        sys.exit('Error creating {0} on {1}\n{2}'.format(
            tests_bare_repo_dir, test_code_dir, e))

    scp_file(email_file_path, config.username, config.host,
             remote_assignment_dir)

    print(assignment, 'uploaded successfully')
    print('Reports repo clone URL:', reports_bare_repo.url)