Add some changes for resolving an obscure bug with user settings
This commit is contained in:
parent
35a3389229
commit
dea78ea189
|
|
@ -2,13 +2,18 @@ from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
|
||||||
from users.forms import CustomUserCreationForm, CustomUserChangeForm
|
from users.forms import CustomUserCreationForm, CustomUserChangeForm
|
||||||
from .models import User, SchoolClass, Role, UserRole, UserSetting, License, UserData, Team
|
from .models import User, SchoolClass, Role, UserRole, UserSetting, License, UserData, Team, SchoolClassMember
|
||||||
|
|
||||||
|
|
||||||
class SchoolClassInline(admin.TabularInline):
|
class SchoolClassInline(admin.TabularInline):
|
||||||
model = SchoolClass.users.through
|
model = SchoolClass.users.through
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
|
# Prevents change permission to reduce loading time on school class page
|
||||||
|
def has_change_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RoleInline(admin.TabularInline):
|
class RoleInline(admin.TabularInline):
|
||||||
model = UserRole
|
model = UserRole
|
||||||
|
|
@ -18,6 +23,8 @@ class RoleInline(admin.TabularInline):
|
||||||
@admin.register(SchoolClass)
|
@admin.register(SchoolClass)
|
||||||
class SchoolClassAdmin(admin.ModelAdmin):
|
class SchoolClassAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'code', 'user_list', 'is_deleted')
|
list_display = ('name', 'code', 'user_list', 'is_deleted')
|
||||||
|
ordering = ('name',)
|
||||||
|
readonly_fields = ['users',]
|
||||||
|
|
||||||
inlines = [
|
inlines = [
|
||||||
SchoolClassInline
|
SchoolClassInline
|
||||||
|
|
@ -49,7 +56,7 @@ class CustomUserAdmin(UserAdmin):
|
||||||
model = User
|
model = User
|
||||||
list_display = ('username', 'first_name', 'last_name', 'school_classes_list', 'is_superuser', 'hep_id')
|
list_display = ('username', 'first_name', 'last_name', 'school_classes_list', 'is_superuser', 'hep_id')
|
||||||
list_filter = ('school_classes', 'is_superuser')
|
list_filter = ('school_classes', 'is_superuser')
|
||||||
ordering = ['pk']
|
ordering = ['username', 'pk']
|
||||||
search_fields = ('username', 'first_name', 'last_name')
|
search_fields = ('username', 'first_name', 'last_name')
|
||||||
autocomplete_fields = ('team',)
|
autocomplete_fields = ('team',)
|
||||||
|
|
||||||
|
|
@ -74,6 +81,8 @@ class UserSettingAdmin(admin.ModelAdmin):
|
||||||
list_display = ('user', 'selected_class')
|
list_display = ('user', 'selected_class')
|
||||||
raw_id_fields = ('user', 'selected_class')
|
raw_id_fields = ('user', 'selected_class')
|
||||||
|
|
||||||
|
search_fields = ('user__email', )
|
||||||
|
|
||||||
|
|
||||||
@admin.register(License)
|
@admin.register(License)
|
||||||
class LicenseAdmin(admin.ModelAdmin):
|
class LicenseAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -97,3 +106,10 @@ class TeamAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
def member_list(self, obj):
|
def member_list(self, obj):
|
||||||
return ', '.join([member.full_name for member in obj.members.all()])
|
return ', '.join([member.full_name for member in obj.members.all()])
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(SchoolClassMember)
|
||||||
|
class SchoolclassMemberAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('user', 'school_class', 'active')
|
||||||
|
list_filter = ('school_class', 'active')
|
||||||
|
search_fields = ('user__email','school_class__name')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from users.validate_user_settings import validate_user_settings
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--dry-run', action='store_true', dest='dry_run', default=True, help='Make dry-run')
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
dry_run = options.get('dry_run')
|
||||||
|
|
||||||
|
validate_user_settings(dry_run=dry_run)
|
||||||
|
|
@ -149,7 +149,7 @@ class User(AbstractUser):
|
||||||
return User.LICENSE_EXPIRED
|
return User.LICENSE_EXPIRED
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['pk', ]
|
ordering = ['username']
|
||||||
|
|
||||||
|
|
||||||
class GroupWithCode(models.Model):
|
class GroupWithCode(models.Model):
|
||||||
|
|
@ -384,3 +384,6 @@ class SchoolClassMember(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
school_class = models.ForeignKey(SchoolClass, on_delete=models.CASCADE)
|
school_class = models.ForeignKey(SchoolClass, on_delete=models.CASCADE)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['user__username']
|
||||||
|
|
|
||||||
|
|
@ -140,17 +140,17 @@ class JoinClass(relay.ClientIDMutation):
|
||||||
school_class = SchoolClass.objects.get(Q(code__iexact=code))
|
school_class = SchoolClass.objects.get(Q(code__iexact=code))
|
||||||
|
|
||||||
if user not in list(school_class.users.all()):
|
if user not in list(school_class.users.all()):
|
||||||
SchoolClassMember.objects.create(user=user, school_class=school_class)
|
SchoolClassMember.objects.get_or_create(user=user, school_class=school_class)
|
||||||
user.set_selected_class(school_class)
|
user.set_selected_class(school_class)
|
||||||
else:
|
else:
|
||||||
raise CodeNotFoundException(
|
raise CodeNotFoundException(
|
||||||
"[CAJ] Schüler ist bereits in Klasse"
|
f"[CAJ] Schüler ist bereits in Klasse"
|
||||||
) # CAJ = Class Already Joined
|
) # CAJ = Class Already Joined
|
||||||
|
|
||||||
return cls(success=True, school_class=school_class)
|
return cls(success=True, school_class=school_class)
|
||||||
except SchoolClass.DoesNotExist:
|
except SchoolClass.DoesNotExist:
|
||||||
raise CodeNotFoundException(
|
raise CodeNotFoundException(
|
||||||
"[CNV] Code ist nicht gültig"
|
f"[CNV] Code ist nicht gültig '{code}'"
|
||||||
) # CNV = Code Not Valid
|
) # CNV = Code Not Valid
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -219,7 +219,7 @@ class CreateSchoolClass(TeacherOnlyMutation):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
school_class = SchoolClass.objects.create(name=name)
|
school_class = SchoolClass.objects.create(name=name)
|
||||||
SchoolClassMember.objects.create(school_class=school_class, user=user)
|
SchoolClassMember.objects.get_or_create(school_class=school_class, user=user)
|
||||||
user.set_selected_class(school_class)
|
user.set_selected_class(school_class)
|
||||||
return cls(result=school_class)
|
return cls(result=school_class)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
|
|
@ -290,7 +290,7 @@ class JoinTeam(TeacherOnlyMutation):
|
||||||
return cls(success=True, team=team)
|
return cls(success=True, team=team)
|
||||||
except Team.DoesNotExist:
|
except Team.DoesNotExist:
|
||||||
raise CodeNotFoundException(
|
raise CodeNotFoundException(
|
||||||
"[CNV] Code ist nicht gültig"
|
f"[CNV] Code ist nicht gültig '{code}'"
|
||||||
) # CNV = Code Not Valid
|
) # CNV = Code Not Valid
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,5 @@ class JoinSchoolClassTest(TestCase):
|
||||||
}
|
}
|
||||||
}, context=self.context)
|
}, context=self.context)
|
||||||
self.assertIsNotNone(executed['errors'])
|
self.assertIsNotNone(executed['errors'])
|
||||||
self.assertEqual(executed['errors'][0]['message'], '[CNV] Code ist nicht gültig')
|
self.assertEqual(executed['errors'][0]['message'], "[CNV] Code ist nicht gültig '1234'")
|
||||||
self.assertEqual(self.user.school_classes.count(), 1)
|
self.assertEqual(self.user.school_classes.count(), 1)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from .models import SchoolClassMember, UserSetting, User
|
||||||
|
|
||||||
|
|
||||||
|
# This code was made for a support case where some users had invalid user settings.
|
||||||
|
def validate_user_setting(user_setting, dry_run=True):
|
||||||
|
|
||||||
|
try:
|
||||||
|
SchoolClassMember.objects.get(user=user_setting.user, school_class=user_setting.selected_class)
|
||||||
|
|
||||||
|
# Test for the invalid user settings and delete them (user settings are gereated automatically)
|
||||||
|
except SchoolClassMember.DoesNotExist as e:
|
||||||
|
print(e)
|
||||||
|
print(f"invalid user setting: {user_setting.user.email} {user_setting.user.selected_class}")
|
||||||
|
if not dry_run:
|
||||||
|
user_id = user_setting.user.id
|
||||||
|
try:
|
||||||
|
del user_setting.user.selected_class
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
user_setting.delete()
|
||||||
|
user = User.objects.get(id=user_id)
|
||||||
|
|
||||||
|
print(f"New user setting: {user.email} {user.selected_class}\n")
|
||||||
|
|
||||||
|
# Errors with SchoolClassMember.MultipleObjectsReturned
|
||||||
|
# A bug created duplicates in the database. This code deletes the duplicates.
|
||||||
|
except SchoolClassMember.MultipleObjectsReturned as e:
|
||||||
|
print(e)
|
||||||
|
count = 0
|
||||||
|
for member in SchoolClassMember.objects.filter(user=user_setting.user):
|
||||||
|
print(f"Multiple SchoolClassMembers for user {count}: {member.id} {member.user.email}: {member.school_class.name} {member.active}")
|
||||||
|
if count >=1:
|
||||||
|
print("delete")
|
||||||
|
if not dry_run:
|
||||||
|
member.delete()
|
||||||
|
count += 1
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
def validate_user_settings(dry_run=True):
|
||||||
|
for user_setting in UserSetting.objects.all():
|
||||||
|
validate_user_setting(user_setting, dry_run=dry_run)
|
||||||
Loading…
Reference in New Issue