diff --git a/backend/delbert.py b/backend/delbert.py index 557260f5c0a257d7d76e6fbfca6304aae899b10c..eb65722209d34956c5c0696d610480b2f778da60 100644 --- a/backend/delbert.py +++ b/backend/delbert.py @@ -2,18 +2,19 @@ import argparse import csv import json import os +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'grady.settings') import secrets import sys import django +django.setup() from django.contrib.auth.models import User import util.importer from core.models import Student, Submission -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'grady.settings') -django.setup() + def parseme(): diff --git a/backend/util/factories.py b/backend/util/factories.py index 953d323d6d306209c953123347fdc8ed3c119e3b..fe14c5e5949c682a4d7c6488393c001e9b6a2e75 100644 --- a/backend/util/factories.py +++ b/backend/util/factories.py @@ -2,13 +2,44 @@ import configparser import secrets from core.models import UserAccount as User, Student, Tutor, Reviewer -from util.importer import PASSWORDS + +STUDENTS = 'students' +TUTORS = 'tutors' +REVIEWERS = 'reviewers' + +PASSWORDS = '.importer_passwords' + +def get_xkcd_password(k=2): + with open('/usr/share/dict/words') as words: + choose_from = list({word.strip().lower() + for word in words if 5 < len(word) < 8}) + + return ''.join(secrets.choice(choose_from) for _ in range(k)) + + +def store_password(username, groupname, password): + storage = configparser.ConfigParser() + storage.read(PASSWORDS) + + if not groupname in storage: + storage[groupname] = {} + + storage[groupname][username] = password + + with open(PASSWORDS, 'w') as passwd_file: + storage.write(passwd_file) + + class GradyUserFactory: - def __init__(self, password_generator_func=get_xkcd_password, *args, **kwargs): + def __init__(self, + password_generator_func=get_xkcd_password, + password_storge=store_password + *args, **kwargs): self.password_generator_func = password_generator_func + self.password_storge = password_storge @staticmethod def _get_random_name(prefix='', suffix='', k=1): @@ -37,7 +68,7 @@ class GradyUserFactory: user.save() if created and store_pw: - store_password(username, groupname, password) + self.password_storge(username, groupname, password) return user @@ -80,31 +111,3 @@ class GradyUserFactory: def make_reviewer(self, username=None, **kwargs): """ Creates or updates a reviewer if needed with defaults """ return self._make_user_generic(username, REVIEWERS, **kwargs) - - -STUDENTS = 'students' -TUTORS = 'tutors' -REVIEWERS = 'reviewers' - - -def get_xkcd_password(k=2): - with open('/usr/share/dict/words') as words: - choose_from = list({word.strip().lower() - for word in words if 5 < len(word) < 8}) - - return ''.join(secrets.choice(choose_from) for _ in range(k)) - - -def store_password(username, groupname, password): - storage = configparser.ConfigParser() - storage.read(PASSWORDS) - - if not groupname in storage: - storage[groupname] = {} - - storage[groupname][username] = password - - with open(PASSWORDS, 'w') as passwd_file: - storage.write(passwd_file) - - diff --git a/backend/util/importer.py b/backend/util/importer.py index 0c77ba2f7123d602201d688c4ca5384f21453ec4..299f4ae547c1b8e1a8a9dcd2568bf3ca29d0b407 100644 --- a/backend/util/importer.py +++ b/backend/util/importer.py @@ -1,18 +1,23 @@ +import configparser import csv import json import os import readline +import secrets from typing import Callable import util.convert import util.processing -from core.models import (ExamType, Feedback, Student, Submission, - SubmissionType, Test) from core.models import UserAccount as User -from util.factories import GradyUserFactory, STUDENTS, TUTORS, REVIEWERS +from core.models import (ExamType, Feedback, Reviewer, Student, Submission, + SubmissionType, Test, Tutor) from util.messages import info, warn from util.processing import EmptyTest +STUDENTS = 'students' +TUTORS = 'tutors' +REVIEWERS = 'reviewers' + HISTFILE = '.importer_history' RECORDS = '.importer' PASSWORDS = '.importer_passwords' @@ -57,6 +62,14 @@ class chdir_context(object): os.chdir(self.old_dir) +def get_xkcd_password(k=2): + with open('/usr/share/dict/words') as words: + choose_from = list({word.strip().lower() + for word in words if 5 < len(word) < 8}) + + return ''.join(secrets.choice(choose_from) for _ in range(k)) + + def i(prompt: str, default: str='', is_path: bool=False, is_file: bool=False): if default is YES or default is NO: answer = valid[input(f'[Q] {prompt} ({default}): ').lower() or ('y' if YES == default else 'n')] @@ -72,8 +85,96 @@ def i(prompt: str, default: str='', is_path: bool=False, is_file: bool=False): return answer -# TODO more factories +def store_password(username, groupname, password): + storage = configparser.ConfigParser() + storage.read(PASSWORDS) + + if not groupname in storage: + storage[groupname] = {} + + storage[groupname][username] = password + + with open(PASSWORDS, 'w') as passwd_file: + storage.write(passwd_file) + + +class GradyUserFactory: + + def __init__(self, password_generator_func=get_xkcd_password, *args, **kwargs): + self.password_generator_func = password_generator_func + + @staticmethod + def _get_random_name(prefix='', suffix='', k=1): + return ''.join((prefix, get_xkcd_password(k), suffix)) + + def _make_base_user(self, username, groupname, store_pw=False, **kwargs): + """ This is a specific wrapper for the django update_or_create method of + objects. + * A new user is created and password and group are set accordingly + * If the user was there before password is NOT change but group is. A + user must only have one group. + + Returns: + (User object, str): The user object that was added to the group and + the password of that user if it was created. + """ + username = username.strip() + user, created = User.objects.update_or_create( + username=username, + defaults=kwargs) + + if created: + password = self.password_generator_func() + user.set_password(password) + user.save() + + if created and store_pw: + store_password(username, groupname, password) + + return user + + def _get_user_model_for_group(self, groupname): + """ Returns the model class for a usergroup """ + return { + STUDENTS: Student, + TUTORS: Tutor, + REVIEWERS: Reviewer, + }[groupname] + + def _make_user_generic(self, username, groupname, **kwargs): + """ Provides a model with a associated user but without any defaults + """ + + if not username: + username = self._get_random_name(prefix=groupname.lower() + '_') + + model = self._get_user_model_for_group(groupname) + user = self._make_base_user(username, groupname, **kwargs) + + generic_user, _ = model.objects.get_or_create(user=user) + + return generic_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_user_generic(username, STUDENTS, **kwargs) + if matrikel_no: + user.objects.update(matrikel_no=matrikel_no) + if exam: + user.objects.update(exam=exam) + return user + + def make_tutor(self, username=None, **kwargs): + """ Creates or updates a tutor if needed with defaults """ + return self._make_user_generic(username, TUTORS, **kwargs) + + def make_reviewer(self, username=None, **kwargs): + """ Creates or updates a reviewer if needed with defaults """ + return self._make_user_generic(username, REVIEWERS, **kwargs) + +# TODO more factories def add_user(username, group, **kwargs): user = GradyUserFactory()._make_base_user(