import configparser import secrets import string from core.models import UserAccount as User from core.models import (ExamType, Feedback, StudentInfo, Submission, SubmissionType) STUDENTS = 'students' TUTORS = 'tutors' REVIEWERS = 'reviewers' PASSWORDS = '.importer_passwords' def get_random_password(length=32): """ Returns a cryptographically random string of specified length """ return ''.join(secrets.choice(string.ascii_lowercase) for _ in range(length)) def store_password(username, groupname, password): storage = configparser.ConfigParser() storage.read(PASSWORDS) if groupname not in storage: storage[groupname] = {} storage[groupname][username] = password with open(PASSWORDS, 'w') as passwd_file: storage.write(passwd_file) class GradyUserFactory: def __init__(self, make_password=get_random_password, password_storge=store_password, *args, **kwargs): self.make_password = make_password self.password_storge = password_storge def _get_random_name(self, prefix='', suffix='', k=4): return ''.join((prefix, self.make_password(k), suffix)) def _get_group_for_user_role(self, role): """ Returns the groupname for a role """ return { 'Student': 'students', 'Tutor': 'tutors', 'Reviewer': 'reviewers' }[role] def _make_base_user(self, username, role, password=None, store_pw=False, **kwargs): """ This is a specific wrapper for the django update_or_create method of objects. * If now username is passed, a generic one will be generated * A new user is created and password and role are set accordingly * If the user was there before password IS changed * A user must only have one role. Returns: (User object, str): The user object that was added to the role and the password of that user if it was created. """ if not username: username = self._get_random_name(prefix=role.lower() + '_') username = username.strip() user, created = User.objects.update_or_create( username=username, role=role, defaults=kwargs) if created: password = self.make_password() if password is None else password user.set_password(password) user.save() if created and store_pw: self.password_storge( username, self._get_group_for_user_role(role), password) return user def make_student(self, username=None, matrikel_no=None, exam=None, **kwargs): """ Creates a student. Defaults can be passed via kwargs like in relation managers objects.update method. """ user = self._make_base_user(username, 'Student', **kwargs) studentInfo = StudentInfo.objects.get_or_create(user=user)[0] if matrikel_no: studentInfo.matrikel_no = matrikel_no if exam: studentInfo.exam = exam studentInfo.save() return user def make_tutor(self, username=None, **kwargs): """ Creates or updates a tutor if needed with defaults """ return self._make_base_user(username, 'Tutor', **kwargs) def make_reviewer(self, username=None, **kwargs): """ Creates or updates a reviewer if needed with defaults """ return self._make_base_user(username, 'Reviewer', **kwargs) def make_exams(exams=[], **kwargs): return [ExamType.objects.get_or_create( module_reference=exam['module_reference'], defaults=exam)[0] for exam in exams] def make_submission_types(submission_types=[], **kwargs): return [SubmissionType.objects.get_or_create( name=submission_type['name'], defaults=submission_type)[0] for submission_type in submission_types] def make_students(students=[], **kwargs): return [GradyUserFactory().make_student( username=student['username'], exam=ExamType.objects.get( module_reference=student['exam']) if 'exam' in student else None ) for student in students] def make_tutors(tutors=[], **kwargs): return [GradyUserFactory().make_tutor(**tutor) for tutor in tutors] def make_reviewers(reviewers=[], **kwargs): return [GradyUserFactory().make_reviewer(**reviewer) for reviewer in reviewers] def make_feedback(feedback, submission_object): feedback['of_tutor'] = User.objects.get( username=feedback['of_tutor']) return Feedback.objects.update_or_create( of_submission=submission_object, defaults=feedback)[0] def make_submissions(submissions=[], **kwargs): submission_objects = [] for submission in submissions: submission_type, _ = SubmissionType.objects.get_or_create( name=submission.get('type', 'Auto generated type')) student, _ = StudentInfo.objects.get_or_create(user=User.objects.get( username=submission.get('user', 'default_user') )) submission_object, _ = Submission.objects.get_or_create( type=submission_type, student=student, defaults={ 'seen_by_student': submission.get('seen_by_student', False), 'text': submission.get('text', ''), }) if 'feedback' in submission: make_feedback(submission['feedback'], submission_object) submission_objects.append(submission_object) return submission_objects def make_test_data(data_dict): return { 'exams': make_exams(**data_dict), 'submission_types': make_submission_types(**data_dict), 'students': make_students(**data_dict), 'tutors': make_tutors(**data_dict), 'reviewers': make_reviewers(**data_dict), 'submissions': make_submissions(**data_dict) } def init_test_instance(): return make_test_data( data_dict={ 'exams': [{ 'module_reference': 'Test Exam 01', 'total_score': 100, 'pass_score': 60, }], 'submission_types': [ { 'name': '01. Sort this or that', 'full_score': 35, 'description': 'Very complicated', 'solution': 'Trivial!' }, { 'name': '02. Merge this or that or maybe even this', 'full_score': 35, 'description': 'Very complicated', 'solution': 'Trivial!' }, { 'name': '03. This one exists for the sole purpose to test', 'full_score': 30, 'description': 'Very complicated', 'solution': 'Trivial!' } ], 'students': [{ 'username': 'student01', 'exam': 'Test Exam 01', }, { 'username': 'student02', 'exam': 'Test Exam 01', }], 'tutors': [{ 'username': 'tutor01' }], 'reviewers': [{ 'username': 'reviewer01', 'password': 'p' }], 'submissions': [ { 'text': 'function blabl\n' ' on multi lines\n' ' for blabla in bla:\n' ' arrrgh\n' ' asasxasx\n' ' lorem ipsum und so\n', 'type': '01. Sort this or that', 'user': 'student01', 'feedback': { 'text': 'Not good!', 'score': 5, 'of_tutor': 'tutor01', 'is_final': True } }, { 'text': 'function blabl\n' ' on multi lines\n' ' for blabla in bla:\n' ' arrrgh\n' ' asasxasx\n' ' lorem ipsum und so\n', 'type': '02. Merge this or that or maybe even this', 'user': 'student01' }, { 'text': 'function blabl\n' ' on multi lines\n' ' for blabla in bla:\n' ' arrrgh\n' ' asasxasx\n' ' lorem ipsum und so\n', 'type': '03. This one exists for the sole purpose to test', 'user': 'student01' }, { 'text': 'function blabl\n' ' on multi lines\n' ' for blabla in bla:\n' ' arrrgh\n' ' asasxasx\n' ' lorem ipsum und so\n', 'type': '03. This one exists for the sole purpose to test', 'user': 'student02' }, ]} )