rewrote user csv import

The import does not delete the existing users anymore, but append the users as new users.
This commit is contained in:
Oskar Hahn 2012-08-10 19:49:46 +02:00
parent bb00eb3eb4
commit 1f2f7cc73a
4 changed files with 84 additions and 186 deletions

View File

@ -10,12 +10,19 @@
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
# for python 2.5 support
from __future__ import with_statement
from random import choice from random import choice
import string import string
import csv
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import transaction
from openslides.utils import csv_ext
from openslides.utils.person import get_person from openslides.utils.person import get_person
from openslides.participant.models import OpenSlidesUser from openslides.participant.models import OpenSlidesUser
@ -47,3 +54,42 @@ def gen_username(first_name, last_name):
User.objects.get(username=testname) User.objects.get(username=testname)
except User.DoesNotExist: except User.DoesNotExist:
return testname return testname
def import_users(csv_file):
error_messages = []
count_success = 0
try:
# check for valid encoding (will raise UnicodeDecodeError if not)
csv_file.read().decode('utf-8')
csv_file.seek(0)
with transaction.commit_on_success():
dialect = csv.Sniffer().sniff(csv_file.readline())
dialect = csv_ext.patchup(dialect)
csv_file.seek(0)
for (line_no, line) in enumerate(csv.reader(csv_file, dialect=dialect)):
if line_no:
try:
(first_name, last_name, gender, category, type, committee, comment) = line[:7]
except ValueError:
error_messages.append(_('Ignoring malformed line %d in import file.') % line_no + 1)
continue
user = OpenSlidesUser()
user.last_name = last_name
user.first_name = first_name
user.username = gen_username(first_name, last_name)
user.gender = gender
user.category = category
user.type = type
user.committee = committee
user.comment = comment
user.firstpassword = gen_password()
user.save()
user.reset_password()
count_success += 1
except csv.Error:
error_messages.appen(_('Import aborted because of severe errors in the input file.'))
except UnicodeDecodeError:
error_messages.appen(_('Import file has wrong character encoding, only UTF-8 is supported!'))
return (count_success, error_messages)

View File

@ -90,9 +90,6 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
class UserImportForm(forms.Form, CssClassMixin): class UserImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}), csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
label=_("CSV File")) label=_("CSV File"))
application_handling = forms.ChoiceField(
required=True, choices=USER_APPLICATION_IMPORT_OPTIONS,
label=_("For existing applications"))
class ConfigForm(forms.Form, CssClassMixin): class ConfigForm(forms.Form, CssClassMixin):

View File

@ -15,7 +15,7 @@ from django.core.urlresolvers import reverse
from openslides.participant.views import ( from openslides.participant.views import (
ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView, ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView,
UserUpdateView, UserDeleteView, SetUserStatusView) UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView)
urlpatterns = patterns('openslides.participant.views', urlpatterns = patterns('openslides.participant.views',
url(r'^$', url(r'^$',
@ -62,7 +62,7 @@ urlpatterns = patterns('openslides.participant.views',
), ),
url(r'^import/$', url(r'^import/$',
'user_import', UserImportView.as_view(),
name='user_import', name='user_import',
), ),

View File

@ -50,7 +50,7 @@ from openslides.utils.views import (
from openslides.config.models import config from openslides.config.models import config
from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup from openslides.participant.models import OpenSlidesUser, OpenSlidesGroup
from openslides.participant.api import gen_username, gen_password from openslides.participant.api import gen_username, gen_password, import_users
from openslides.participant.forms import ( from openslides.participant.forms import (
UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm, UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm,
UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm) UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm)
@ -300,6 +300,41 @@ class ParticipantsPasswordsPDF(PDFView):
story.append(t) story.append(t)
class UserImportView(FormView):
"""
Import Users via csv.
"""
permission_required = 'participant.can_manage_participant'
template_name = 'participant/import.html'
form_class = UserImportForm
def form_valid(self, form):
# check for valid encoding (will raise UnicodeDecodeError if not)
success, error_messages = import_users(self.request.FILES['csvfile'])
for message in error_messages:
messages.error(self.request, message)
if success:
messages.success(self.request, _('%d new participants were successfully imported.') % success)
return super(UserImportView, self).form_valid(form)
@permission_required('participant.can_manage_participant')
def reset_password(request, user_id):
"""
Reset the Password.
"""
user = User.objects.get(pk=user_id)
if request.method == 'POST':
user.profile.reset_password()
messages.success(request,
_('The Password for <b>%s</b> was successfully reset.') % user)
else:
gen_confirm_form(request,
_('Do you really want to reset the password for <b>%s</b>?') % user,
reverse('user_reset_password', args=[user_id]))
return redirect(reverse('user_edit', args=[user_id]))
@login_required @login_required
@template('participant/settings.html') @template('participant/settings.html')
def user_settings(request): def user_settings(request):
@ -344,186 +379,6 @@ def user_settings_password(request):
} }
@permission_required('participant.can_manage_participant')
@template('participant/import.html')
def user_import(request):
"""
Import Users via csv.
"""
from openslides.application.models import Application
try:
request.user.profile
messages.error(request, _('The import function is available for the admin (without user profile) only.'))
return redirect(reverse('user_overview'))
except Profile.DoesNotExist:
pass
except AttributeError:
# AnonymousUser
pass
if request.method == 'POST':
form = UserImportForm(request.POST, request.FILES)
if form.is_valid():
try:
# check for valid encoding (will raise UnicodeDecodeError if not)
request.FILES['csvfile'].read().decode('utf-8')
request.FILES['csvfile'].seek(0)
with transaction.commit_on_success():
old_users = {}
applications_mapped = 0
applications_review = 0
applications_removed = 0
try:
janitor = User.objects.get(username='__system__.janitor')
except User.DoesNotExist:
janitor = User()
janitor.first_name = ''
janitor.last_name = ''
janitor.username = '__system__.janitor'
janitor.save()
applications = Application.objects.all()
for application in applications:
if form.cleaned_data['application_handling'] == 'DISCARD':
# need to do this explicit since some applications may belong
# to __system__.janitor which is a permanent user
application.delete(force=True)
applications_removed += 1
else:
# collect all applications and map them to their submitters
submitter = application.submitter
skey = '%s_%s' % (submitter.first_name, submitter.last_name)
if not skey in old_users:
old_users[skey] = []
old_users[skey].append(application.id)
application.submitter = janitor
application.save()
if application.supporter.all():
application.writelog(_('Supporters removed after user import.'), user=request.user)
profiles = Profile.objects.all()
for profile in profiles:
profile.user.delete()
profile.delete()
i = -1
dialect = csv.Sniffer().sniff(request.FILES['csvfile'].readline())
dialect = csv_ext.patchup(dialect)
request.FILES['csvfile'].seek(0)
for (lno, line) in enumerate(csv.reader(request.FILES['csvfile'], dialect=dialect)):
i += 1
if i > 0:
try:
(first_name, last_name, gender, group, type, committee, comment) = line[:7]
except ValueError:
messages.error(request, _('Ignoring malformed line %d in import file.') % (lno + 1))
i -= 1
continue
user = User()
user.last_name = last_name
user.first_name = first_name
user.username = gen_username(first_name, last_name)
#user.email = email
user.save()
profile = Profile()
profile.user = user
profile.gender = gender
profile.group = group
profile.type = type
profile.committee = committee
profile.comment = comment
profile.firstpassword = gen_password()
profile.user.set_password(profile.firstpassword)
profile.user.save()
profile.save()
if type == 'delegate':
delegate = Group.objects.get(name='Delegierter')
user.groups.add(delegate)
else:
observer = Group.objects.get(name='Beobachter')
user.groups.add(observer)
if form.cleaned_data['application_handling'] == 'REASSIGN':
# live remap
skey = '%s_%s' % (user.first_name, user.last_name)
if skey in old_users:
for appid in old_users[skey]:
try:
application = Application.objects.get(id=appid)
application.submitter = user
application.save()
application.writelog(_('Reassigned to "%s" after (re)importing users.') % ("%s %s" % (user.first_name, user.last_name)), user=request.user)
applications_mapped += 1
except Application.DoesNotExist:
messages.error(request, _('Could not reassing application %d - object not found!') % appid)
del old_users[skey]
if old_users:
# mark all applications without a valid user as 'needs review'
# this will account for *all* applications if application_mode == 'INREVIEW'
for skey in old_users:
for appid in old_users[skey]:
try:
application = Application.objects.get(id=appid)
if application.status != 'rev':
application.set_status(user=request.user, status='rev', force=True)
applications_review += 1
except Application.DoesNotExist:
messages.error(request, _('Could not reassing application %d - object not found!') % appid)
if applications_review:
messages.warning(request, ungettext('%d application could not be reassigned and needs a review!',
'%d applications could not be reassigned and need a review!', applications_review) % applications_review)
if applications_mapped:
messages.success(request, ungettext('%d application was successfully reassigned.',
'%d applications were successfully reassigned.', applications_mapped) % applications_mapped)
if applications_removed:
messages.warning(request, ungettext('%d application was discarded.',
'%d applications were discarded.', applications_removed) % applications_removed)
if i > 0:
messages.success(request, _('%d new participants were successfully imported.') % i)
return redirect(reverse('user_overview'))
except csv.Error:
message.error(request, _('Import aborted because of severe errors in the input file.'))
except UnicodeDecodeError:
messages.error(request, _('Import file has wrong character encoding, only UTF-8 is supported!'))
else:
messages.error(request, _('Please check the form for errors.'))
else:
messages.warning(request, _("Attention: All existing participants will be removed if you import new participants."))
if Application.objects.all():
messages.warning(request, _("Attention: Supporters from all existing applications will be removed."))
messages.warning(request, _("Attention: Applications which can't be mapped to new users will be set to 'Needs Review'."))
form = UserImportForm()
return {
'form': form,
}
@permission_required('participant.can_manage_participant')
def reset_password(request, user_id):
"""
Reset the Password.
"""
user = User.objects.get(pk=user_id)
if request.method == 'POST':
user.profile.reset_password()
messages.success(request,
_('The Password for <b>%s</b> was successfully reset.') % user)
else:
gen_confirm_form(request,
_('Do you really want to reset the password for <b>%s</b>?') % user,
reverse('user_reset_password', args=[user_id]))
return redirect(reverse('user_edit', args=[user_id]))
def login(request): def login(request):
extra_content = {} extra_content = {}