Create subgroup for enrolled students so when creating projects we do not need the exported list of students and can use the subgroup instead.

This commit is contained in:
Tim Schubert 2018-09-20 15:53:40 +02:00
parent 01bfb44c73
commit 5d01c74440
5 changed files with 117 additions and 57 deletions

View file

@ -18,7 +18,7 @@ Operations 1 and 2 require super user privileges to the API. The rest don't.
4. Set up the project for the example solutions and the student projects. If you have pre-existing example solutions place them in `<some_course>/solutions/solutions`.
```
# abgabesystem projects -c <some_course> -d <deploy key> -s <students.csv>
# abgabesystem projects -c <some_course> -d <deploy key>
```
5. Add all administrative users (e.g. users supervising the course or checking homework solutions) to the group of the course.

View file

@ -1,19 +1,24 @@
import os
import subprocess
import subprocess
import logging as log
from .students import Student, create_user, get_students
from .students import Student, create_user, get_students, enroll_student, get_student_group
from .projects import create_tag, setup_course
from gitlab.exceptions import GitlabCreateError, GitlabGetError
def create_users(gl, args):
def enroll_students(gl, args):
"""Creates Gitlab users from exported students list
"""
student_group = get_student_group(gl, args.course)
with open(args.students, encoding='iso8859') as students_csv:
for student in Student.from_csv(students_csv):
try:
create_user(gl, student, args.ldap_base, args.ldap_provider)
user = create_user(gl, student, args.ldap_base, args.ldap_provider)
# TODO this is ugly, should be group of course, but python-gitlab does not cache the query
enroll_student(gl, user, student_group)
except GitlabCreateError:
log.warn('Failed to create user: %s' % student.user)
@ -85,7 +90,7 @@ def course(gl, args):
"""Creates the group for the course
"""
try:
group = gl.groups.create({
gl.groups.create({
'name': args.course,
'path': args.course,
'visibility': 'internal',

View file

@ -1,4 +1,10 @@
import logging as log
from gitlab import DEVELOPER_ACCESS
from gitlab.exceptions import GitlabError, GitlabCreateError
from .students import enrolled_students
from .course import InvalidCourse
def create_tag(project, tag, ref):
"""Creates protected tag on ref
@ -60,7 +66,7 @@ def create_project(gl, group, user, reference, deploy_key):
try:
subgroup.members.create({
'user_id': user.id,
'access_level': gitlab.DEVELOPER_ACCESS,
'access_level': DEVELOPER_ACCESS,
})
except GitlabError:
log.warning('Failed to add student %s to its own group' % user.username)
@ -71,55 +77,47 @@ def create_project(gl, group, user, reference, deploy_key):
log.warning(e.error_message)
def setup_course(gl, group, students_csv, deploy_key):
def create_reference_solution(gl, namespace):
reference_project = gl.projects.create({
'name': 'solutions',
'namespace_id': namespace,
'visibility': 'internal',
})
reference_project.commits.create({
'branch': 'master',
'commit_message': 'Initial commit',
'actions': [
{
'action': 'create',
'file_path': 'README.md',
'content': 'Example solutions go here',
},
]
})
return reference_project
def setup_projects(gl, course, deploy_key):
"""Sets up the internal structure for the group for use with the course
"""
solution = None
reference_project = None
try:
solution = gl.groups.create({
'name': 'solutions',
'path': 'solutions',
'parent_id': group.id,
'visibility': 'internal',
})
except GitlabCreateError as e:
log.info('Failed to create solutions group. %s' % e.error_message)
solutions = group.subgroups.list(search='solutions')
if len(solutions) > 0 and solutions[0].name == 'solutions':
solution = gl.groups.get(solutions[0].id, lazy=True)
else:
raise(GitlabCreateError(error_message='Failed to setup solutions subgroup'))
solutions = None
solutions_groups = course.subgroups.list(search='solutions')
for group in solutions_groups:
if group.name == 'solutions':
solutions = group
try:
reference_project = gl.projects.create({
'name': 'solutions',
'namespace_id': solution.id,
'visibility': 'internal',
})
reference_project.commits.create({
'branch': 'master',
'commit_message': 'Initial commit',
'actions': [
{
'action': 'create',
'file_path': 'README.md',
'content': 'Example solutions go here',
},
]
})
except GitlabCreateError as e:
log.info('Failed to setup group structure. %s' % e.error_message)
projects = solution.projects.list(search='solutions')
if len(projects) > 0 and projects[0].name == 'solutions':
reference_project = gl.projects.get(projects[0].id)
else:
raise(GitlabCreateError(error_message='Failed to setup reference solutions'))
if solutions is None:
raise InvalidCourse("No solutions subgroup")
if solution is None or reference_project is None:
raise(GitlabCreateError(error_message='Failed to setup course'))
reference_projects = solutions.projects.list(search='solutions')
for project in reference_projects:
if project.name == 'solutions':
reference_project = gl.projects.get(project.id)
for user in get_students(gl, students_csv):
create_project(gl, solution, user, reference_project, deploy_key)
if reference_project is None:
reference_project = create_reference_solution(gl, solutions.id)
for user in enrolled_students(gl, course):
create_project(gl, solutions, user, reference_project, deploy_key)

View file

@ -1,4 +1,13 @@
import csv
import secrets
class MissingStudentsGroup(Exception):
pass
class MissingCourseGroup(Exception):
pass
class Student():
@ -24,7 +33,7 @@ class Student():
+ ' ' + line['Nachname'], line['Gruppe'])
def get_students(gl, students_csv):
def get_students_csv(gl, students_csv):
"""Returns already existing GitLab users for students from provided CSV file that have an account.
"""
@ -34,6 +43,24 @@ def get_students(gl, students_csv):
yield users[0]
def enrolled_students(gl, course):
"""Returns the students enrolled in the course
"""
students = None
for group in course.subgroups.list(search='students'):
if group.name == 'students':
students = group
if students is None:
raise MissingStudentsGroup()
# get all members excluding inherited members
students = gl.groups.get(students.id)
for member in students.members.list():
yield gl.users.get(member.id)
def create_user(gl, student, ldap_base, ldap_provider):
"""Creates a GitLab user account student.
Requires admin privileges.
@ -52,3 +79,33 @@ def create_user(gl, student, ldap_base, ldap_provider):
return user
def get_student_group(gl, course_name):
"""Gets the `students` subgroup for the course
"""
course = None
for g in gl.groups.list(search=course_name):
if g.name == course_name:
course = g
if course is None:
raise MissingCourseGroup()
students_group = None
for g in course.subgroups.list(search='students'):
if g.name == 'students':
students_group = gl.groups.get(g.id)
if students_group is None:
raise MissingStudentsGroup()
return students_group
def enroll_student(gl, user, group):
"""Adds a student to the course
"""
pass

View file

@ -4,7 +4,7 @@ import gitlab
import argparse
import logging as log
from abgabesystem.commands import create_users, projects, deadline, plagiates, course
from abgabesystem.commands import enroll_students, projects, deadline, plagiates, course
if __name__ == '__main__':
@ -17,9 +17,10 @@ if __name__ == '__main__':
user_parser = subparsers.add_parser(
'users',
help='Creates users from LDAP')
user_parser.set_defaults(func=create_users)
help='Creates users and enrolls them in the course')
user_parser.set_defaults(func=enroll_students)
user_parser.add_argument('-s', '--students', dest='students')
user_parser.add_argument('-c', '--course', dest='course')
user_parser.add_argument('-b', '--ldap-base', dest='ldap_base')
user_parser.add_argument('-p', '--ldap-provider', dest='ldap_provider')
@ -35,7 +36,6 @@ if __name__ == '__main__':
projects_parser.set_defaults(func=projects)
projects_parser.add_argument('-c', '--course', dest='course')
projects_parser.add_argument('-d', '--deploy-key', dest='deploy_key')
projects_parser.add_argument('-s', '--students', dest='students')
deadline_parser = subparsers.add_parser(
'deadline',