import re from datetime import datetime import string import random from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractUser, Permission from django.core.exceptions import ObjectDoesNotExist from django.db import models from django.utils.translation import ugettext_lazy as _ from core.hep_client import HepClient from users.managers import RoleManager, UserRoleManager, UserManager, LicenseManager DEFAULT_SCHOOL_ID = 1 class User(AbstractUser): last_module = models.ForeignKey('books.Module', related_name='+', on_delete=models.SET_NULL, null=True) recent_modules = models.ManyToManyField('books.Module', related_name='+', through='books.RecentModule') last_topic = models.ForeignKey('books.Topic', related_name='+', on_delete=models.SET_NULL, null=True) avatar_url = models.CharField(max_length=254, blank=True, default='') email = models.EmailField(_('email address'), unique=True) hep_id = models.PositiveIntegerField(null=True, blank=False) hep_group_id = models.PositiveIntegerField(null=True, blank=False) license_expiry_date = models.DateField(blank=False, null=True, default=None) objects = UserManager() def get_role_permissions(self): perms = set() for role in Role.objects.get_roles_for_user(self): perms = perms.union( ('{}.{}'.format(r.content_type.app_label, r.codename) for r in role.role_permission.all()) ) return perms def get_all_permissions(self, obj=None): """ works as long as we have only a single school :param obj: :return: django permissions and school permissions for single default school """ django_permissions = super().get_all_permissions(obj) return django_permissions.union(self.get_role_permissions()) def has_perm(self, perm, obj=None): return super(User, self).has_perm(perm, obj) or perm in self.get_all_permissions(obj) def users_in_same_school_class(self): return User.objects.filter(school_classes__users=self.pk) def get_teacher(self): if self.is_teacher(): return self elif self.school_classes.count() > 0: return self.school_classes.first().get_teacher() else: return None def is_teacher(self): return self.user_roles.filter(role__key='teacher').exists() def selected_class(self): try: settings = UserSetting.objects.get(user=self) return settings.selected_class except ObjectDoesNotExist: if self.school_classes.count() > 0: default_selected_class = self.school_classes.first() UserSetting.objects.create(selected_class=default_selected_class, user=self) return default_selected_class else: return None def sync_with_hep_data(self, hep_data): data_has_changed = False if self.email != hep_data['email']: self.email = hep_data['email'] self.username = hep_data['email'] data_has_changed = True if self.first_name != hep_data['firstname']: self.first_name = hep_data['firstname'] data_has_changed = True if self.last_name != hep_data['lastname']: self.last_name = hep_data['lastname'] data_has_changed = True if data_has_changed: self.save() def set_selected_class(self, school_class): user_settings, created = UserSetting.objects.get_or_create(user=self) user_settings.selected_class = school_class user_settings.save() @property def full_name(self): return self.get_full_name() class SchoolClass(models.Model): name = models.CharField(max_length=100, blank=False, null=False, unique=True) is_deleted = models.BooleanField(blank=False, null=False, default=False) users = models.ManyToManyField(get_user_model(), related_name='school_classes', blank=True, through='users.SchoolClassMember') code = models.CharField('Code zum Beitreten', blank=True, null=True, max_length=10, unique=True, default=None) class Meta: verbose_name = 'Schulklasse' verbose_name_plural = 'Schulklassen' def __str__(self): return '{}'.format(self.name) @classmethod def generate_default_group_name(cls, user=None): prefix = 'Meine Klasse' if user is not None: return f'{prefix} {user.pk}' prefix_regex = r'Meine Klasse (\d+)' initial_default_group = '{} 1'.format(prefix) my_group_filter = cls.objects.filter(name__startswith=prefix).order_by('-pk') if len(my_group_filter) == 0: return initial_default_group match = re.search(prefix_regex, my_group_filter[0].name) if not match: return initial_default_group index = int(match.group(1)) return '{} {}'.format(prefix, index + 1) @classmethod def create_default_group_for_teacher(cls, user): default_class_name = cls.generate_default_group_name() default_class = cls.objects.create(name=default_class_name) SchoolClassMember.objects.create( user=user, school_class=default_class ) def is_user_in_schoolclass(self, user): return user.is_superuser or user.school_classes.filter(pk=self.id).count() > 0 def get_teacher(self): return self.users.filter(user_roles__role__key='teacher').first() def generate_code(self): letters = string.ascii_lowercase digits = string.digits code = ''.join(random.choice(letters) for i in range(4)) + ''.join(random.choice(digits) for i in range(2)) try: SchoolClass.objects.get(code=code) self.generate_code() except SchoolClass.DoesNotExist: self.code = code.upper() self.save() def save(self, *args, **kwargs): if self.code == '': # '' can't be unique, so we null it self.code = None super().save(*args, **kwargs) class Role(models.Model): key = models.CharField(_('Key'), max_length=100, blank=False, null=False, unique=True) name = models.CharField(_('Name'), max_length=100, blank=False, null=False) role_permission = models.ManyToManyField(Permission, verbose_name=_('Role permission'), blank=True, related_name="role_set", related_query_name="role") objects = RoleManager() def __str__(self): return self.name def permissions_as_code(self): return self.role_permission.values_list('codename', flat=True) def has_permission(self, permission_name): try: self.role_permission.get(codename=permission_name) return True except Permission.DoesNotExist: return False @staticmethod def create_key(name): return name.lower() class Meta: permissions = ( # ("can_edit_events", "Can edit events"), # ("can_edit_own_comments", "Can edit own comments"), # ("can_delete_comments", "Can delete comments"), ('can_manage_school_class_content', 'Can manage contents for assigned school clases'), # ("can_admin_school", "Can admin school"), ) class UserRole(models.Model): user = models.ForeignKey(User, blank=False, null=False, on_delete=models.CASCADE, related_name='user_roles') role = models.ForeignKey(Role, blank=False, null=False, on_delete=models.CASCADE, related_name='user_roles') objects = UserRoleManager() @property def groups(self): return SchoolClass.objects.filter(users=self.user.id) @property def group_ids(self): return map(lambda g: g.id, self.groups) @classmethod def get_roles_for_user(cls, user): roles = UserRole.objects.filter(user=user) return roles @classmethod def get_role_for_user(cls, user): return UserRole.get_roles_for_user(user).first() def __str__(self): return '%s: %s' % (self.role, self.user) class UserSetting(models.Model): user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='user_setting') selected_class = models.ForeignKey(SchoolClass, blank=True, null=True, on_delete=models.CASCADE) class UserData(models.Model): user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='user_data') accepted_terms = models.BooleanField(default=False) class License(models.Model): for_role = models.ForeignKey(Role, blank=False, null=True, on_delete=models.CASCADE) licensee = models.ForeignKey(User, blank=False, null=True, on_delete=models.CASCADE) expire_date = models.DateField(blank=False, null=True, ) order_id = models.IntegerField(blank=False, null=False, default=-1) raw = models.TextField(default='') objects = LicenseManager() def is_teacher_license(self): return self.for_role.key == RoleManager.TEACHER_KEY def is_valid(self): return HepClient.is_product_active( datetime(self.expire_date.year, self.expire_date.month, self.expire_date.day), self.for_role.key) def __str__(self): return f'License for role: {self.for_role}' class SchoolClassMember(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) school_class = models.ForeignKey(SchoolClass, on_delete=models.CASCADE) active = models.BooleanField(default=True)