diff --git a/openslides/participant/api.py b/openslides/participant/api.py
index d2919821b..84077a0e4 100644
--- a/openslides/participant/api.py
+++ b/openslides/participant/api.py
@@ -10,12 +10,19 @@
:license: GNU GPL, see LICENSE for more details.
"""
+# for python 2.5 support
+from __future__ import with_statement
+
from random import choice
import string
+import csv
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.participant.models import OpenSlidesUser
@@ -47,3 +54,42 @@ def gen_username(first_name, last_name):
User.objects.get(username=testname)
except User.DoesNotExist:
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)
diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py
index ebd267bc0..7da694a58 100644
--- a/openslides/participant/forms.py
+++ b/openslides/participant/forms.py
@@ -90,9 +90,6 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
class UserImportForm(forms.Form, CssClassMixin):
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
label=_("CSV File"))
- application_handling = forms.ChoiceField(
- required=True, choices=USER_APPLICATION_IMPORT_OPTIONS,
- label=_("For existing applications"))
class ConfigForm(forms.Form, CssClassMixin):
diff --git a/openslides/participant/urls.py b/openslides/participant/urls.py
index 61751402c..51ae2e301 100644
--- a/openslides/participant/urls.py
+++ b/openslides/participant/urls.py
@@ -15,7 +15,7 @@ from django.core.urlresolvers import reverse
from openslides.participant.views import (
ParticipantsListPDF, ParticipantsPasswordsPDF, Overview, UserCreateView,
- UserUpdateView, UserDeleteView, SetUserStatusView)
+ UserUpdateView, UserDeleteView, SetUserStatusView, UserImportView)
urlpatterns = patterns('openslides.participant.views',
url(r'^$',
@@ -62,7 +62,7 @@ urlpatterns = patterns('openslides.participant.views',
),
url(r'^import/$',
- 'user_import',
+ UserImportView.as_view(),
name='user_import',
),
diff --git a/openslides/participant/views.py b/openslides/participant/views.py
index e0169ae5c..a0879c002 100644
--- a/openslides/participant/views.py
+++ b/openslides/participant/views.py
@@ -50,7 +50,7 @@ from openslides.utils.views import (
from openslides.config.models import config
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 (
UserCreateForm, UserUpdateForm, OpenSlidesUserForm, UsersettingsForm,
UserImportForm, GroupForm, AdminPasswordChangeForm, ConfigForm)
@@ -300,6 +300,41 @@ class ParticipantsPasswordsPDF(PDFView):
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 %s was successfully reset.') % user)
+ else:
+ gen_confirm_form(request,
+ _('Do you really want to reset the password for %s?') % user,
+ reverse('user_reset_password', args=[user_id]))
+ return redirect(reverse('user_edit', args=[user_id]))
+
+
@login_required
@template('participant/settings.html')
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 %s was successfully reset.') % user)
- else:
- gen_confirm_form(request,
- _('Do you really want to reset the password for %s?') % user,
- reverse('user_reset_password', args=[user_id]))
- return redirect(reverse('user_edit', args=[user_id]))
-
def login(request):
extra_content = {}