From 1fe99021444bcbc20e4e243ba3aa105af7442e37 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 27 Jan 2012 00:40:30 -0400 Subject: [PATCH 1/4] Set new users' password as unusable until one is set, add column to user list showing if password is usable --- apps/user_management/views.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/user_management/views.py b/apps/user_management/views.py index b9c9a6c9e8..85fea6a198 100644 --- a/apps/user_management/views.py +++ b/apps/user_management/views.py @@ -42,8 +42,11 @@ def user_list(request): { 'name': _(u'active'), 'attribute': encapsulate(lambda x: two_state_template(x.is_active)), - } - + }, + { + 'name': _(u'has usable password?'), + 'attribute': encapsulate(lambda x: two_state_template(x.has_usable_password())), + }, ], 'multi_select_as_buttons': True, }, @@ -82,7 +85,9 @@ def user_add(request): if request.method == 'POST': form = UserForm(request.POST) if form.is_valid(): - user = form.save() + user = form.save(commit=False) + user.set_unusable_password() + user.save() messages.success(request, _(u'User "%s" created successfully.') % user) return HttpResponseRedirect(reverse('user_set_password', args=[user.pk])) else: From ff88e1a422b022002d2f698561ec84e2ea22b7cb Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 27 Jan 2012 00:41:15 -0400 Subject: [PATCH 2/4] Add command line function to import a list users from a CSV file --- apps/user_management/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/import_users.py | 83 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 apps/user_management/management/__init__.py create mode 100644 apps/user_management/management/commands/__init__.py create mode 100644 apps/user_management/management/commands/import_users.py diff --git a/apps/user_management/management/__init__.py b/apps/user_management/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_management/management/commands/__init__.py b/apps/user_management/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_management/management/commands/import_users.py b/apps/user_management/management/commands/import_users.py new file mode 100644 index 0000000000..b02d08d5b4 --- /dev/null +++ b/apps/user_management/management/commands/import_users.py @@ -0,0 +1,83 @@ +from __future__ import absolute_import + +import csv, os, sys +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError, LabelCommand +from django.utils.simplejson import loads, dumps +from django.contrib.auth.models import User +from django.db.utils import IntegrityError + + +def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): + # csv.py doesn't do Unicode; encode temporarily as UTF-8: + csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), + dialect=dialect, **kwargs) + for row in csv_reader: + # decode UTF-8 back to Unicode, cell by cell: + yield [unicode(cell, 'utf-8') for cell in row] + +def utf_8_encoder(unicode_csv_data): + for line in unicode_csv_data: + yield line.encode('utf-8') + + +class Command(LabelCommand): + args = '' + help = 'Import users from a CSV file with the field order: username, firstname, lastname, email.' + option_list = LabelCommand.option_list + ( + make_option('--noinput', action='store_false', dest='interactive', + default=True, help='Do not ask the user for confirmation before ' + 'starting.'), + make_option('--password', action='store', dest='password', + help='The default password to assign to each new user.'), + make_option('--skip-repeated', action='store_true', dest='skip_repeated', + default=False, help='Don\'t exit if the user already exists.'), + ) + + def handle_label(self, label, **options): + if not os.access(label, os.R_OK): + raise CommandError("File '%s' is not readable." % label) + + if options['password']: + default_password = options['password'] + else: + default_password = None + + if _confirm(options['interactive']) == 'yes': + print 'Beginning import...' + with open(label, 'rb') as f: + reader = unicode_csv_reader(f) + try: + for row in reader: + print 'Adding: %s' % ', '.join(row) + try: + user = User( + username=row[0], + first_name=row[1], + last_name=row[2], + email=row[3] + ) + user.set_password(default_password) + user.save() + except IntegrityError: + print 'Repeated user entry: %s' % ', '.join(row) + if options['skip_repeated']: + print 'Ignoring.' + else: + sys.exit() + + except csv.Error, e: + sys.exit('file %s, line %d: %s' % (label, reader.line_num, e)) + else: + print 'Finish.' + else: + print 'Cancelled.' + + +def _confirm(interactive): + if not interactive: + return 'yes' + return raw_input('You have requested to import a number of users from a CSV file.\n' + 'Are you sure you want to do this?\n' + 'Type \'yes\' to continue, or any other value to cancel: ') From 70aec4a0b7a5ec00bc7506013c7bb557ec59e67a Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 27 Jan 2012 00:46:13 -0400 Subject: [PATCH 3/4] Add information about the user CSV importer to the documentation --- docs/releases/0.12.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/releases/0.12.rst b/docs/releases/0.12.rst index 621145bdee..fab6ac8d7d 100644 --- a/docs/releases/0.12.rst +++ b/docs/releases/0.12.rst @@ -97,12 +97,28 @@ follow:: **Optional arguments** -* The ``--noinput`` argument skips confirmation and starts the upload inmediately. +* The ``--noinput`` argument skips confirmation and starts the upload immediately. * The ``--metadata`` argument allows specifing what metadata will be assigned to the documents when uploaded. * And the ``--document_type`` applies a previously defined document type to the uploaded documents. +Out of process user import +~~~~~~~~~~~~~~~~~~~~~~~~~~ +A management command has been added to import a large number users +from a CSV file. The command line options for this feature are as +follow:: + + $ ./manage.py import_users --noinput --password=welcome123 --skip-repeated user_list.csv + +**Optional arguments** + +* The ``--noinput`` argument skips confirmation and starts the import immediately. +* The ``--password`` argument allows specifing what default password will be assigned + to all the new users that are imported. +* The ``--skip-repeated`` tells the importedr to not stop when finding + that a user already exists in the database. + Upgrading from a previous version ================================= From 0b92fad9c223b80102487d3562f273fce705d249 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 27 Jan 2012 00:47:30 -0400 Subject: [PATCH 4/4] Add information about the CSV field order --- docs/releases/0.12.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/releases/0.12.rst b/docs/releases/0.12.rst index fab6ac8d7d..965e12617e 100644 --- a/docs/releases/0.12.rst +++ b/docs/releases/0.12.rst @@ -111,6 +111,9 @@ follow:: $ ./manage.py import_users --noinput --password=welcome123 --skip-repeated user_list.csv +The CSV field order must be: username, first name, last name and email, any other +column after those is ignored. + **Optional arguments** * The ``--noinput`` argument skips confirmation and starts the import immediately.