Skip to content

Commit

Permalink
First version of user merging, fixes troeger#47. Somehow. Initially.
Browse files Browse the repository at this point in the history
  • Loading branch information
troeger committed Oct 19, 2015
1 parent f6c64d8 commit b652618
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 4 deletions.
33 changes: 32 additions & 1 deletion web/opensubmit/admin/user.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
# User admin interface
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.contrib.admin import StackedInline
from opensubmit.models import UserProfile
from opensubmit.models import UserProfile, tutor_courses
from django.shortcuts import render
from django.contrib import admin
from django.http import HttpResponseRedirect
from django.contrib import messages
from django.core.urlresolvers import reverse



class UserProfileInline(StackedInline):
model = UserProfile
classes = ('grp-collapse grp-open',)
inline_classes = ('grp-collapse grp-open',)

def social(user):
'''
Returns the social ID of this user.
'''
try:
return str(user.social_auth.get().uid)
except:
return None

class UserAdmin(DjangoUserAdmin):
actions = ['mergeusers',]

inlines = (UserProfileInline, )
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', social)

def mergeusers(modeladmin, request, queryset):
if len(queryset) != 2:
modeladmin.message_user(request, "Please choose exactly two users to merge.", level=messages.WARNING)
return reverse('admin:index')
# In most cases, we want to keep the user logged in recently, so we query accordingly
primary, secondary = queryset.order_by('-date_joined')
if len(tutor_courses(primary)) > 0 or len(tutor_courses(secondary)) > 0:
# Since the user is deleted, this is more complicated (course ownership, gradings given etc.)
modeladmin.message_user(request, "Merging course owners or tutors is not support at the moment.", level=messages.WARNING)
return reverse('admin:index')
return HttpResponseRedirect('%s?primary_id=%u&secondary_id=%s'%(reverse('mergeusers'), primary.pk, secondary.pk))
mergeusers.short_description = "Merge selected users"
28 changes: 26 additions & 2 deletions web/opensubmit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import string
import unicodedata

from django.db import models
from django.db import models, transaction
from django.contrib.auth.models import User, Group
from django.utils import timezone
from django.core.mail import send_mail, EmailMessage
Expand Down Expand Up @@ -204,6 +204,8 @@ def user_unicode(self):
return u'%s %s (%s@...)' % (self.first_name, self.last_name, shortened)
elif self.first_name or self.last_name:
return u'%s %s' % (self.first_name, self.last_name)
elif self.username:
return u'%s' % (self.username)
else:
return u'User %u' % (self.pk)
User.__unicode__ = user_unicode
Expand Down Expand Up @@ -231,6 +233,28 @@ def tutor_courses(user):
'''
return list(chain(user.courses_tutoring.all().filter(active__exact=True), user.courses.all().filter(active__exact=True)))

@transaction.atomic
def move_user_data(primary, secondary):
'''
Moves all submissions and other data linked to the secondary user into the primary user.
Nothing is deleted here, we just modify foreign user keys.
'''
# Update all submission authorships of the secondary to the primary
submissions = Submission.objects.filter(authors__id=secondary.pk)
for subm in submissions:
if subm.submitter == secondary:
subm.submitter = primary;
subm.authors.remove(secondary)
subm.authors.add(primary)
subm.save()
# Transfer course registrations
try:
for course in secondary.profile.courses.all():
primary.profile.courses.add(course)
primary.profile.save()
except UserProfile.DoesNotExist:
# That's a database consistency problem, but he will go away anyway
pass

class ValidSubmissionFileManager(models.Manager):
'''
Expand Down Expand Up @@ -377,7 +401,7 @@ class Submission(models.Model):

assignment = models.ForeignKey(Assignment, related_name='submissions')
submitter = models.ForeignKey(User, related_name='submitted')
authors = models.ManyToManyField(User, related_name='authored')
authors = models.ManyToManyField(User, related_name='authored') # includes also submitter, see submission_post_save() handler
notes = models.TextField(max_length=200, blank=True)
file_upload = models.ForeignKey(SubmissionFile, related_name='submissions', blank=True, null=True, verbose_name='New upload')
created = models.DateTimeField(auto_now_add=True, editable=False)
Expand Down
10 changes: 10 additions & 0 deletions web/opensubmit/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
url(r'^machines/$', 'opensubmit.api.machines', name='machines'),
url(r'^settings/$', 'opensubmit.views.settings', name='settings'),
url(r'^courses/$', 'opensubmit.views.courses', name='courses'),
url(r'^mergeusers/$', 'opensubmit.views.mergeusers', name='mergeusers'),

url(r'^grappelli/', include('grappelli.urls')), # grappelli URLS
url('', include('social.apps.django_app.urls', namespace='social')),
Expand All @@ -36,3 +37,12 @@
# on production systems, both static and media files must be served directly by Apache
urlpatterns += staticfiles_urlpatterns()
urlpatterns += static(MEDIA_URL_RELATIVE, document_root=MEDIA_ROOT)

import urls
def show_urls(urllist, depth=0):
for entry in urllist:
print " " * depth, entry.regex.pattern
if hasattr(entry, 'url_patterns'):
show_urls(entry.url_patterns, depth + 1)

show_urls(urls.urlpatterns)
24 changes: 23 additions & 1 deletion web/opensubmit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

from forms import SettingsForm, getSubmissionForm, SubmissionFileUpdateForm, MailForm
from models import user_courses, SubmissionFile, Submission, Assignment, TestMachine, Course, UserProfile, db_fixes
from models import inform_student, inform_course_owner, open_assignments
from models import inform_student, inform_course_owner, open_assignments,move_user_data
from settings import JOB_EXECUTOR_SECRET, MAIN_URL


Expand Down Expand Up @@ -180,6 +180,28 @@ def update(request, subm_id):
return render(request, 'update.html', {'submissionFileUpdateForm': updateForm,
'submission': submission})

@login_required
@staff_member_required
def mergeusers(request):
'''
Offers an intermediate admin view to merge existing users.
'''
if request.method == 'POST':
primary=get_object_or_404(User, pk=request.POST['primary_id'])
secondary=get_object_or_404(User, pk=request.POST['secondary_id'])
try:
move_user_data(primary, secondary)
messages.info(request, 'Submissions moved to user %u.'%(primary.pk))
except:
messages.error(request, 'Error during data migration, nothing changed.')
return redirect('admin:index')
messages.info(request, 'User %u deleted.'%(secondary.pk))
secondary.delete()
return redirect('admin:index')
primary=get_object_or_404(User, pk=request.GET['primary_id'])
secondary=get_object_or_404(User, pk=request.GET['secondary_id'])
# Determine data to be migrated
return render(request, 'mergeusers.html', {'primary': primary, 'secondary': secondary})

@login_required
@staff_member_required
Expand Down

0 comments on commit b652618

Please sign in to comment.