Unverified Commit 6939a581 by Brian Mesick Committed by GitHub

Merge pull request #16384 from edx/bmedx/django111_student_mgmt_cmds

Student management command cleanup for Django 1.11
parents 7cd3e723 ce50c9e6
from optparse import make_option from __future__ import print_function
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
class Command(BaseCommand): class Command(BaseCommand):
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option('--list', parser.add_argument('name_or_email',
action='store_true', help='Username or email address of the user to add or remove')
dest='list', parser.add_argument('group_name',
default=False, help='Name of the group to change')
help='List available groups'), parser.add_argument('--list',
make_option('--create', action='store_true',
action='store_true', help='List available groups')
dest='create', parser.add_argument('--create',
default=False, action='store_true',
help='Create the group if it does not exist'), help='Create the group if it does not exist')
make_option('--remove', parser.add_argument('--remove',
action='store_true', action='store_true',
dest='remove', help='Remove the user from the group instead of adding it')
default=False,
help='Remove the user from the group instead of adding it'),
)
args = '<user|email> <group>'
help = 'Add a user to a group' help = 'Add a user to a group'
def print_groups(self): def print_groups(self):
print 'Groups available:' print('Groups available:')
for group in Group.objects.all().distinct(): for group in Group.objects.all().distinct():
print ' ', group.name print(' {}'.format(group.name))
def handle(self, *args, **options): def handle(self, *args, **options):
if options['list']: if options['list']:
self.print_groups() self.print_groups()
return return
if len(args) != 2: name_or_email = options['name_or_email']
raise CommandError('Usage is add_to_group {0}'.format(self.args)) group_name = options['group_name']
name_or_email, group_name = args
if '@' in name_or_email: if '@' in name_or_email:
user = User.objects.get(email=name_or_email) user = User.objects.get(email=name_or_email)
...@@ -60,4 +54,4 @@ class Command(BaseCommand): ...@@ -60,4 +54,4 @@ class Command(BaseCommand):
else: else:
user.groups.add(group) user.groups.add(group)
print 'Success!' print('Success!')
...@@ -20,23 +20,17 @@ from opaque_keys.edx.keys import CourseKey ...@@ -20,23 +20,17 @@ from opaque_keys.edx.keys import CourseKey
class Command(BaseCommand): class Command(BaseCommand):
"""Add our handler to the space where django-admin looks up commands.""" """Add our handler to the space where django-admin looks up commands."""
# TODO: revisit now that rake has been deprecated
# It appears that with the way Rake invokes these commands, we can't
# have more than one arg passed through...annoying.
args = ("course_id", )
help = """Export a CSV mapping usernames to anonymized ids help = """Export a CSV mapping usernames to anonymized ids
Exports a CSV document mapping each username in the specified course to Exports a CSV document mapping each username in the specified course to
the anonymized, unique user ID. the anonymized, unique user ID.
""" """
def handle(self, *args, **options): def add_arguments(self, parser):
if len(args) != 1: parser.add_argument('course_id')
raise CommandError("Usage: unique_id_mapping %s" %
" ".join(("<%s>" % arg for arg in Command.args)))
course_key = CourseKey.from_string(args[0]) def handle(self, *args, **options):
course_key = CourseKey.from_string(options['course_id'])
# Generate the output filename from the course ID. # Generate the output filename from the course ID.
# Change slashes to dashes first, and then append .csv extension. # Change slashes to dashes first, and then append .csv extension.
......
from __future__ import print_function
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -11,22 +13,27 @@ from textwrap import dedent ...@@ -11,22 +13,27 @@ from textwrap import dedent
import json import json
from pytz import UTC from pytz import UTC
# Examples:
# python manage.py assigngroups summary_test:0.3,skip_summary_test:0.7 log.txt "Do previews of future materials help?"
# python manage.py assigngroups skip_capacitor:0.3,capacitor:0.7 log.txt "Do we show capacitor in linearity tutorial?"
def group_from_value(groups, v): def group_from_value(groups, v):
''' Given group: (('a',0.3),('b',0.4),('c',0.3)) And random value """
Given group: (('a',0.3),('b',0.4),('c',0.3)) And random value
in [0,1], return the associated group (in the above case, return in [0,1], return the associated group (in the above case, return
'a' if v<0.3, 'b' if 0.3<=v<0.7, and 'c' if v>0.7 'a' if v<0.3, 'b' if 0.3<=v<0.7, and 'c' if v>0.7
''' """
sum = 0 curr_sum = 0
for (g, p) in groups: for (group, p_value) in groups:
sum = sum + p curr_sum = curr_sum + p_value
if sum > v: if curr_sum > v:
return g return group
return g # For round-off errors return group # For round-off errors
class Command(BaseCommand): class Command(BaseCommand):
help = dedent("""\ help = dedent("""
Assign users to test groups. Takes a list of groups: Assign users to test groups. Takes a list of groups:
a:0.3,b:0.4,c:0.3 file.txt "Testing something" a:0.3,b:0.4,c:0.3 file.txt "Testing something"
Will assign each user to group a, b, or c with Will assign each user to group a, b, or c with
...@@ -36,48 +43,49 @@ class Command(BaseCommand): ...@@ -36,48 +43,49 @@ class Command(BaseCommand):
Will log what happened to file.txt. Will log what happened to file.txt.
""") """)
def handle(self, *args, **options): def add_arguments(self, parser):
if len(args) != 3: parser.add_argument('group_and_score')
print "Invalid number of options" parser.add_argument('log_name')
sys.exit(-1) parser.add_argument('description')
def handle(self, *args, **options):
# Extract groups from string # Extract groups from string
group_strs = [x.split(':') for x in args[0].split(',')] group_strs = [x.split(':') for x in options['group_and_score'].split(',')]
groups = [(group, float(value)) for group, value in group_strs] groups = [(group, float(value)) for group, value in group_strs]
print "Groups", groups print("Groups", groups)
## Confirm group probabilities add up to 1 # Confirm group probabilities add up to 1
total = sum(zip(*groups)[1]) total = sum(zip(*groups)[1])
print "Total:", total print("Total:", total)
if abs(total - 1) > 0.01: if abs(total - 1) > 0.01:
print "Total not 1" print("Total not 1")
sys.exit(-1) sys.exit(-1)
## Confirm groups don't already exist # Confirm groups don't already exist
for group in dict(groups): for group in dict(groups):
if UserTestGroup.objects.filter(name=group).count() != 0: if UserTestGroup.objects.filter(name=group).count() != 0:
print group, "already exists!" print(group, "already exists!")
sys.exit(-1) sys.exit(-1)
group_objects = {} group_objects = {}
f = open(args[1], "a+") f = open(options['log_name'], "a+")
## Create groups # Create groups
for group in dict(groups): for group in dict(groups):
utg = UserTestGroup() utg = UserTestGroup()
utg.name = group utg.name = group
utg.description = json.dumps({"description": args[2]}, utg.description = json.dumps({"description": options['description']},
{"time": datetime.datetime.now(UTC).isoformat()}) {"time": datetime.datetime.now(UTC).isoformat()})
group_objects[group] = utg group_objects[group] = utg
group_objects[group].save() group_objects[group].save()
## Assign groups # Assign groups
users = list(User.objects.all()) users = list(User.objects.all())
count = 0 count = 0
for user in users: for user in users:
if count % 1000 == 0: if count % 1000 == 0:
print count print(count)
count = count + 1 count = count + 1
v = random.uniform(0, 1) v = random.uniform(0, 1)
group = group_from_value(groups, v) group = group_from_value(groups, v)
...@@ -88,10 +96,7 @@ class Command(BaseCommand): ...@@ -88,10 +96,7 @@ class Command(BaseCommand):
group=group group=group
).encode('utf-8')) ).encode('utf-8'))
## Save groups # Save groups
for group in group_objects: for group in group_objects:
group_objects[group].save() group_objects[group].save()
f.close() f.close()
# python manage.py assigngroups summary_test:0.3,skip_summary_test:0.7 log.txt "Do previews of future materials help?"
# python manage.py assigngroups skip_capacitor:0.3,capacitor:0.7 log.txt "Do we show capacitor in linearity tutorial?"
...@@ -6,6 +6,7 @@ from django.db import transaction ...@@ -6,6 +6,7 @@ from django.db import transaction
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from optparse import make_option from optparse import make_option
from six import text_type
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from course_modes.models import CourseMode from course_modes.models import CourseMode
...@@ -31,54 +32,36 @@ class Command(BaseCommand): ...@@ -31,54 +32,36 @@ class Command(BaseCommand):
Without the --commit option, the command will have no effect. Without the --commit option, the command will have no effect.
""" """
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option( group = parser.add_mutually_exclusive_group()
'-f', '--from_mode', group.add_argument(
dest='from_mode',
default=None,
help='move from this enrollment mode'
),
make_option(
'-t', '--to_mode',
dest='to_mode',
default=None,
help='move to this enrollment mode'
),
make_option(
'-c', '--course', '-c', '--course',
dest='course', help='The course to change enrollments in')
default=None, group.add_argument(
help='the course to change enrollments in'
),
make_option(
'-o', '--org', '-o', '--org',
dest='org', help='All courses belonging to this org will be selected for changing the enrollments')
default=None,
help='all courses belonging to this org will be selected for changing the enrollments' parser.add_argument(
), '-f', '--from_mode',
make_option( required=True,
help='Move from this enrollment mode')
parser.add_argument(
'-t', '--to_mode',
required=True,
help='Move to this enrollment mode')
parser.add_argument(
'--commit', '--commit',
action='store_true', action='store_true',
dest='commit', help='Save the changes, without this flag only a dry run will be performed and nothing will be changed')
default=False,
help='display what will be done without any effect'
)
)
def handle(self, *args, **options): def handle(self, *args, **options):
course_id = options.get('course') course_id = options['course']
org = options.get('org') org = options['org']
from_mode = options.get('from_mode') from_mode = options['from_mode']
to_mode = options.get('to_mode') to_mode = options['to_mode']
commit = options.get('commit') commit = options['commit']
if (not course_id and not org) or (course_id and org):
raise CommandError('You must provide either a course ID or an org, but not both.')
if from_mode is None or to_mode is None:
raise CommandError('Both `from` and `to` course modes must be given.')
course_keys = [] course_keys = []
if course_id: if course_id:
try: try:
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
...@@ -111,7 +94,7 @@ class Command(BaseCommand): ...@@ -111,7 +94,7 @@ class Command(BaseCommand):
commit (bool): required to make the change to the database. Otherwise commit (bool): required to make the change to the database. Otherwise
just a count will be displayed. just a count will be displayed.
""" """
unicode_course_key = unicode(course_key) unicode_course_key = text_type(course_key)
if CourseMode.mode_for_course(course_key, to_mode) is None: if CourseMode.mode_for_course(course_key, to_mode) is None:
logger.info('Mode ({}) does not exist for course ({}).'.format(to_mode, unicode_course_key)) logger.info('Mode ({}) does not exist for course ({}).'.format(to_mode, unicode_course_key))
return return
......
from django.core.management.base import BaseCommand, CommandError from __future__ import print_function
import csv
import os import os
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from student.models import UserProfile from student.models import UserProfile
import csv
class Command(BaseCommand): class Command(BaseCommand):
help = """ help = """
Sets or gets certificate restrictions for users Sets or gets certificate restrictions for users
from embargoed countries. (allow_certificate in from embargoed countries. (allow_certificate in
...@@ -31,79 +33,62 @@ class Command(BaseCommand): ...@@ -31,79 +33,62 @@ class Command(BaseCommand):
""" """
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option('-i', '--import', # This command can only take one of these arguments per run, this enforces that.
metavar='IMPORT_FILE', group = parser.add_mutually_exclusive_group(required=True)
dest='import', group.add_argument('-i', '--import',
default=False, metavar='IMPORT_FILE',
help='csv file to import, comma delimitted file with ' nargs='?',
'double-quoted entries'), help='CSV file to import, comma delimitted file with double-quoted entries')
make_option('-o', '--output', group.add_argument('-o', '--output',
metavar='EXPORT_FILE', metavar='EXPORT_FILE',
dest='output', nargs='?',
default=False, help='CSV file to export')
help='csv file to export'), group.add_argument('-e', '--enable',
make_option('-e', '--enable', metavar='STUDENT',
metavar='STUDENT', nargs='?',
dest='enable', help='Enable a certificate for a single student')
default=False, group.add_argument('-d', '--disable',
help="enable a single student's certificate"), metavar='STUDENT',
make_option('-d', '--disable', nargs='?',
metavar='STUDENT', help='Disable a certificate for a single student')
dest='disable',
default=False,
help="disable a single student's certificate")
)
def handle(self, *args, **options): def handle(self, *args, **options):
if options['output']: if options['output']:
if os.path.exists(options['output']): if os.path.exists(options['output']):
raise CommandError("File {0} already exists".format( raise CommandError("File {0} already exists".format(options['output']))
options['output'])) disabled_users = UserProfile.objects.filter(allow_certificate=False)
disabled_users = UserProfile.objects.filter(
allow_certificate=False)
with open(options['output'], 'w') as csvfile: with open(options['output'], 'w') as csvfile:
csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"', csvwriter = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
quoting=csv.QUOTE_MINIMAL)
for user in disabled_users: for user in disabled_users:
csvwriter.writerow([user.user.username]) csvwriter.writerow([user.user.username])
print('{} disabled users written'.format(len(disabled_users)))
elif options['import']: elif options['import']:
if not os.path.exists(options['import']): if not os.path.exists(options['import']):
raise CommandError("File {0} does not exist".format( raise CommandError("File {0} does not exist".format(options['import']))
options['import']))
print "Importing students from {0}".format(options['import']) print("Importing students from {0}".format(options['import']))
students = None
with open(options['import']) as csvfile: with open(options['import']) as csvfile:
student_list = csv.reader(csvfile, delimiter=',', student_list = csv.reader(csvfile, delimiter=',', quotechar='"')
quotechar='"')
students = [student[0] for student in student_list] students = [student[0] for student in student_list]
if not students: if not students:
raise CommandError( raise CommandError("Unable to read student data from {0}".format(options['import']))
"Unable to read student data from {0}".format(
options['import']))
UserProfile.objects.filter(user__username__in=students).update(
allow_certificate=False)
elif options['enable']: update_cnt = UserProfile.objects.filter(user__username__in=students).update(allow_certificate=False)
print('{} user(s) disabled out of {} in CSV file'.format(update_cnt, len(students)))
print "Enabling {0} for certificate download".format( elif options['enable']:
options['enable']) print("Enabling {0} for certificate download".format(options['enable']))
cert_allow = UserProfile.objects.get( cert_allow = UserProfile.objects.get(user__username=options['enable'])
user__username=options['enable'])
cert_allow.allow_certificate = True cert_allow.allow_certificate = True
cert_allow.save() cert_allow.save()
elif options['disable']: elif options['disable']:
print("Disabling {0} for certificate download".format(options['disable']))
print "Disabling {0} for certificate download".format( cert_allow = UserProfile.objects.get(user__username=options['disable'])
options['disable'])
cert_allow = UserProfile.objects.get(
user__username=options['disable'])
cert_allow.allow_certificate = False cert_allow.allow_certificate = False
cert_allow.save() cert_allow.save()
...@@ -4,8 +4,8 @@ import logging ...@@ -4,8 +4,8 @@ import logging
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db import transaction from django.db import transaction
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from optparse import make_option
from student.models import CourseEnrollment, User from student.models import CourseEnrollment, User
...@@ -33,64 +33,60 @@ class Command(BaseCommand): ...@@ -33,64 +33,60 @@ class Command(BaseCommand):
Or Or
$ ... change_enrollment -e "joe@example.com,frank@example.com,bill@example.com" -c some/course/id --from audit --to honor $ ... change_enrollment -e "joe@example.com,frank@example.com,..." -c some/course/id --from audit --to honor
See what would have been changed from audit to honor without making that change See what would have been changed from audit to honor without making that change
$ ... change_enrollment -u joe,frank,bill -c some/course/id --from audit --to honor -n $ ... change_enrollment -u joe,frank,bill -c some/course/id --from audit --to honor -n
""" """
option_list = BaseCommand.option_list + ( enrollment_modes = ('audit', 'verified', 'honor')
make_option('-f', '--from',
metavar='FROM_MODE', def add_arguments(self, parser):
dest='from_mode', parser.add_argument('-f', '--from',
default=False, metavar='FROM_MODE',
help='move from this enrollment mode'), dest='from_mode',
make_option('-t', '--to', required=True,
metavar='TO_MODE', choices=self.enrollment_modes,
dest='to_mode', help='Move from this enrollment mode')
default=False, parser.add_argument('-t', '--to',
help='move to this enrollment mode'), metavar='TO_MODE',
make_option('-u', '--usernames', dest='to_mode',
metavar='USERNAME', required=True,
dest='username', choices=self.enrollment_modes,
default=False, help='Move to this enrollment mode')
help="Comma-separated list of usernames to move in the course"), parser.add_argument('-u', '--username',
make_option('-e', '--emails', metavar='USERNAME',
metavar='EMAIL', help='Comma-separated list of usernames to move in the course')
dest='email', parser.add_argument('-e', '--email',
default=False, metavar='EMAIL',
help="Comma-separated list of email addresses to move in the course"), help='Comma-separated list of email addresses to move in the course')
make_option('-c', '--course', parser.add_argument('-c', '--course',
metavar='COURSE_ID', metavar='COURSE_ID',
dest='course_id', dest='course_id',
default=False, required=True,
help="course id to use for transfer"), help='Course id to use for transfer')
make_option('-n', '--noop', parser.add_argument('-n', '--noop',
action='store_true', action='store_true',
dest='noop', help='Display what will be done but do not actually do anything')
default=False,
help="display what will be done but don't actually do anything")
)
def handle(self, *args, **options): def handle(self, *args, **options):
error_users = [] try:
success_users = [] course_key = CourseKey.from_string(options['course_id'])
except InvalidKeyError:
raise CommandError('Invalid or non-existant course id {}'.format(options['course_id']))
if not options['course_id']: if not options['username'] and not options['email']:
raise CommandError('You must specify a course id for this command') raise CommandError('You must include usernames (-u) or emails (-e) to select users to update')
if not options['from_mode'] or not options['to_mode']:
raise CommandError('You must specify a "to" and "from" mode as parameters')
course_key = CourseKey.from_string(options['course_id'])
enrollment_args = dict( enrollment_args = dict(
course_id=course_key, course_id=course_key,
mode=options['from_mode'] mode=options['from_mode']
) )
error_users = []
success_users = []
if options['username']: if options['username']:
self.update_enrollments('username', enrollment_args, options, error_users, success_users) self.update_enrollments('username', enrollment_args, options, error_users, success_users)
...@@ -102,8 +98,10 @@ class Command(BaseCommand): ...@@ -102,8 +98,10 @@ class Command(BaseCommand):
def update_enrollments(self, identifier, enrollment_args, options, error_users, success_users): def update_enrollments(self, identifier, enrollment_args, options, error_users, success_users):
""" Update enrollments for a specific user identifier (email or username). """ """ Update enrollments for a specific user identifier (email or username). """
users = options[identifier].split(",") users = options[identifier].split(",")
for identified_user in users: for identified_user in users:
logger.info(identified_user) logger.info(identified_user)
try: try:
user_args = { user_args = {
identifier: identified_user identifier: identified_user
......
""" """
A script to create some dummy users A script to create some dummy users
""" """
from __future__ import print_function
import uuid import uuid
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
...@@ -32,6 +33,7 @@ def create(num, course_key): ...@@ -32,6 +33,7 @@ def create(num, course_key):
(user, _, _) = _do_create_account(make_random_form()) (user, _, _) = _do_create_account(make_random_form())
if course_key is not None: if course_key is not None:
CourseEnrollment.enroll(user, course_key) CourseEnrollment.enroll(user, course_key)
print('Created user {}'.format(user.username))
class Command(BaseCommand): class Command(BaseCommand):
...@@ -45,16 +47,15 @@ Examples: ...@@ -45,16 +47,15 @@ Examples:
create_random_users.py 100 HarvardX/CS50x/2012 create_random_users.py 100 HarvardX/CS50x/2012
""" """
def handle(self, *args, **options): def add_arguments(self, parser):
if len(args) < 1 or len(args) > 2: parser.add_argument('num_users',
print Command.help help='Number of users to create',
return type=int)
parser.add_argument('course_key',
num = int(args[0]) help='Add newly created users to this course',
nargs='?')
if len(args) == 2:
course_key = CourseKey.from_string(args[1])
else:
course_key = None
def handle(self, *args, **options):
num = options['num_users']
course_key = CourseKey.from_string(options['course_key']) if options['course_key'] else None
create(num, course_key) create(num, course_key)
from optparse import make_option from __future__ import print_function
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils import translation from django.utils import translation
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
...@@ -23,56 +22,39 @@ class Command(TrackedCommand): ...@@ -23,56 +22,39 @@ class Command(TrackedCommand):
manage.py ... create_user -e test@example.com -p insecure -c edX/Open_DemoX/edx_demo_course -m verified manage.py ... create_user -e test@example.com -p insecure -c edX/Open_DemoX/edx_demo_course -m verified
""" """
option_list = BaseCommand.option_list + ( def add_arguments(self, parser):
make_option('-m', '--mode', parser.add_argument('-m', '--mode',
metavar='ENROLLMENT_MODE', metavar='ENROLLMENT_MODE',
dest='mode', default='honor',
default='honor', choices=('audit', 'verified', 'honor'),
choices=('audit', 'verified', 'honor'), help='Enrollment type for user for a specific course, defaults to "honor"')
help='Enrollment type for user for a specific course'), parser.add_argument('-u', '--username',
make_option('-u', '--username', metavar='USERNAME',
metavar='USERNAME', help='Username, defaults to "user" in the email')
dest='username', parser.add_argument('-n', '--name',
default=None, metavar='NAME',
help='Username, defaults to "user" in the email'), help='Name, defaults to "user" in the email')
make_option('-n', '--name', parser.add_argument('-p', '--password',
metavar='NAME', metavar='PASSWORD',
dest='name', help='Password for user',
default=None, required=True)
help='Name, defaults to "user" in the email'), parser.add_argument('-e', '--email',
make_option('-p', '--password', metavar='EMAIL',
metavar='PASSWORD', help='Email for user',
dest='password', required=True)
default=None, parser.add_argument('-c', '--course',
help='Password for user'), metavar='COURSE_ID',
make_option('-e', '--email', help='Course to enroll the user in (optional)')
metavar='EMAIL', parser.add_argument('-s', '--staff',
dest='email', action='store_true',
default=None, help='Give user the staff bit, defaults to off')
help='Email for user'),
make_option('-c', '--course',
metavar='COURSE_ID',
dest='course',
default=None,
help='course to enroll the user in (optional)'),
make_option('-s', '--staff',
dest='staff',
default=False,
action='store_true',
help='give user the staff bit'),
)
def handle(self, *args, **options): def handle(self, *args, **options):
username = options['username'] username = options['username'] if options['username'] else options['email'].split('@')[0]
name = options['name'] name = options['name'] if options['name'] else options['email'].split('@')[0]
if not username:
username = options['email'].split('@')[0]
if not name:
name = options['email'].split('@')[0]
# parse out the course into a coursekey # parse out the course into a coursekey
if options['course']: course = CourseKey.from_string(options['course']) if options['course'] else None
course = CourseKey.from_string(options['course'])
form = AccountCreationForm( form = AccountCreationForm(
data={ data={
...@@ -83,11 +65,13 @@ class Command(TrackedCommand): ...@@ -83,11 +65,13 @@ class Command(TrackedCommand):
}, },
tos_required=False tos_required=False
) )
# django.utils.translation.get_language() will be used to set the new # django.utils.translation.get_language() will be used to set the new
# user's preferred language. This line ensures that the result will # user's preferred language. This line ensures that the result will
# match this installation's default locale. Otherwise, inside a # match this installation's default locale. Otherwise, inside a
# management command, it will always return "en-us". # management command, it will always return "en-us".
translation.activate(settings.LANGUAGE_CODE) translation.activate(settings.LANGUAGE_CODE)
try: try:
user, _, reg = _do_create_account(form) user, _, reg = _do_create_account(form)
if options['staff']: if options['staff']:
...@@ -97,8 +81,10 @@ class Command(TrackedCommand): ...@@ -97,8 +81,10 @@ class Command(TrackedCommand):
reg.save() reg.save()
create_comments_service_user(user) create_comments_service_user(user)
except AccountValidationError as e: except AccountValidationError as e:
print e.message print(e.message)
user = User.objects.get(email=options['email']) user = User.objects.get(email=options['email'])
if options['course']:
if course:
CourseEnrollment.enroll(user, course, mode=options['mode']) CourseEnrollment.enroll(user, course, mode=options['mode'])
translation.deactivate() translation.deactivate()
...@@ -14,7 +14,7 @@ class Command(BaseCommand): ...@@ -14,7 +14,7 @@ class Command(BaseCommand):
This command back-populates domain of the site the user account was created on. This command back-populates domain of the site the user account was created on.
""" """
help = """./manage.py lms populate_created_on_site_user_attribute --users <user_id1>,<user_id2>... help = """./manage.py lms populate_created_on_site_user_attribute --users <user_id1>,<user_id2>...
'--activation-keys <key1>,<key2>... --site-domain <site_domain> --settings=devstack""" '--activation-keys <key1>,<key2>... --site-domain <site_domain> --settings=devstack_docker"""
def add_arguments(self, parser): def add_arguments(self, parser):
""" """
...@@ -35,6 +35,7 @@ class Command(BaseCommand): ...@@ -35,6 +35,7 @@ class Command(BaseCommand):
parser.add_argument( parser.add_argument(
'--site-domain', '--site-domain',
help='Enter an existing site domain.', help='Enter an existing site domain.',
required=True
) )
def handle(self, *args, **options): def handle(self, *args, **options):
...@@ -42,8 +43,6 @@ class Command(BaseCommand): ...@@ -42,8 +43,6 @@ class Command(BaseCommand):
user_ids = options['users'].split(',') if options['users'] else [] user_ids = options['users'].split(',') if options['users'] else []
activation_keys = options['activation_keys'].split(',') if options['activation_keys'] else [] activation_keys = options['activation_keys'].split(',') if options['activation_keys'] else []
if not site_domain:
raise CommandError('You must provide site-domain argument.')
if not user_ids and not activation_keys: if not user_ids and not activation_keys:
raise CommandError('You must provide user ids or activation keys.') raise CommandError('You must provide user ids or activation keys.')
......
from optparse import make_option from __future__ import print_function
import re
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
import re
class Command(BaseCommand): class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--unset',
action='store_true',
dest='unset',
default=False,
help='Set is_staff to False instead of True'),
)
args = '<user|email> [user|email ...]>'
help = """ help = """
This command will set is_staff to true for one or more users. This command will set is_staff to true for one or more users.
Lookup by username or email address, assumes usernames Lookup by username or email address, assumes usernames
do not look like email addresses. do not look like email addresses.
""" """
def handle(self, *args, **options): def add_arguments(self, parser):
if len(args) < 1: parser.add_argument('users',
raise CommandError('Usage is set_staff {0}'.format(self.args)) nargs='+',
help='Users to set or unset (with the --unset flag) as superusers')
parser.add_argument('--unset',
action='store_true',
dest='unset',
default=False,
help='Set is_staff to False instead of True')
for user in args: def handle(self, *args, **options):
if re.match(r'[^@]+@[^@]+\.[^@]+', user): for user in options['users']:
try: try:
if re.match(r'[^@]+@[^@]+\.[^@]+', user):
v = User.objects.get(email=user) v = User.objects.get(email=user)
except: else:
raise CommandError("User {0} does not exist".format(user))
else:
try:
v = User.objects.get(username=user) v = User.objects.get(username=user)
except:
raise CommandError("User {0} does not exist".format(user))
if options['unset']: if options['unset']:
v.is_staff = False v.is_staff = False
else: else:
v.is_staff = True v.is_staff = True
v.save()
print('Modified {} sucessfully.'.format(user))
v.save() except Exception as err: # pylint: disable=broad-except
print("Error modifying user with identifier {}: {}: {}".format(user, type(err).__name__, err.message))
print 'Success!' print('Complete!')
"""Management command to grant or revoke superuser access for one or more users""" """Management command to grant or revoke superuser access for one or more users"""
from __future__ import print_function
from optparse import make_option
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
class Command(BaseCommand): class Command(BaseCommand):
"""Management command to grant or revoke superuser access for one or more users""" """Management command to grant or revoke superuser access for one or more users"""
option_list = BaseCommand.option_list + (
make_option('--unset',
action='store_true',
dest='unset',
default=False,
help='Set is_superuser to False instead of True'),
)
args = '<user|email> [user|email ...]>'
help = """ help = """
This command will set is_superuser to true for one or more users. This command will set is_superuser to true for one or more users.
Lookup by username or email address, assumes usernames Lookup by username or email address, assumes usernames
do not look like email addresses. do not look like email addresses.
""" """
def handle(self, *args, **options): def add_arguments(self, parser):
if len(args) < 1: parser.add_argument('users',
raise CommandError('Usage is set_superuser {0}'.format(self.args)) nargs='+',
help='Users to set or unset (with the --unset flag) as superusers')
parser.add_argument('--unset',
action='store_true',
dest='unset',
default=False,
help='Set is_superuser to False instead of True')
for user in args: def handle(self, *args, **options):
for user in options['users']:
try: try:
if '@' in user: if '@' in user:
userobj = User.objects.get(email=user) userobj = User.objects.get(email=user)
...@@ -39,8 +38,9 @@ class Command(BaseCommand): ...@@ -39,8 +38,9 @@ class Command(BaseCommand):
userobj.is_superuser = True userobj.is_superuser = True
userobj.save() userobj.save()
print('Modified {} sucessfully.'.format(user))
except Exception as err: # pylint: disable=broad-except except Exception as err: # pylint: disable=broad-except
print "Error modifying user with identifier {}: {}: {}".format(user, type(err).__name__, err.message) print("Error modifying user with identifier {}: {}: {}".format(user, type(err).__name__, err.message))
print 'Success!' print('Complete!')
"""Tests for the bulk_change_enrollment command.""" """Tests for the bulk_change_enrollment command."""
import ddt import ddt
from six import text_type
from django.core.management import call_command from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from mock import patch, call from mock import patch, call
...@@ -35,12 +37,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -35,12 +37,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
# Verify that no users are in the `from` mode yet. # Verify that no users are in the `from` mode yet.
self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0)
args = '--course {course} --from_mode {from_mode} --to_mode {to_mode} --commit'.format(
course=text_type(self.course.id),
from_mode=from_mode,
to_mode=to_mode
)
call_command( call_command(
'bulk_change_enrollment', 'bulk_change_enrollment',
course=unicode(self.course.id), *args.split(' ')
from_mode=from_mode,
to_mode=to_mode,
commit=True,
) )
# Verify that all users have been moved -- if not, this will # Verify that all users have been moved -- if not, this will
...@@ -67,12 +72,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -67,12 +72,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0)
self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0)
call_command( args = '--org {org} --from_mode {from_mode} --to_mode {to_mode} --commit'.format(
'bulk_change_enrollment',
org=self.org, org=self.org,
from_mode=from_mode, from_mode=from_mode,
to_mode=to_mode, to_mode=to_mode
commit=True, )
call_command(
'bulk_change_enrollment',
*args.split(' ')
) )
# Verify that all users have been moved -- if not, this will # Verify that all users have been moved -- if not, this will
...@@ -91,7 +99,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -91,7 +99,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
call_command( call_command(
'bulk_change_enrollment', 'bulk_change_enrollment',
org=self.org, org=self.org,
course=unicode(self.course.id), course=text_type(self.course.id),
from_mode='audit', from_mode='audit',
to_mode='no-id-professional', to_mode='no-id-professional',
commit=True, commit=True,
...@@ -114,12 +122,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -114,12 +122,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=self.course.id)), 0)
self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0) self.assertEqual(len(CourseEnrollment.objects.filter(mode=to_mode, course_id=course_2.id)), 0)
call_command( args = '--org {org} --from_mode {from_mode} --to_mode {to_mode} --commit'.format(
'bulk_change_enrollment',
org=self.org, org=self.org,
from_mode=from_mode, from_mode=from_mode,
to_mode=to_mode, to_mode=to_mode
commit=True, )
call_command(
'bulk_change_enrollment',
*args.split(' ')
) )
# Verify that users were not moved for the invalid course/mode combination # Verify that users were not moved for the invalid course/mode combination
...@@ -139,12 +150,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -139,12 +150,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
CourseModeFactory(course_id=self.course.id, mode_slug='no-id-professional') CourseModeFactory(course_id=self.course.id, mode_slug='no-id-professional')
with self.assertRaises(CommandError): with self.assertRaises(CommandError):
call_command( args = '--org {org} --from_mode {from_mode} --to_mode {to_mode} --commit'.format(
'bulk_change_enrollment',
org='fakeX', org='fakeX',
from_mode='audit', from_mode='audit',
to_mode='no-id-professional', to_mode='no-id-professional',
commit=True, )
call_command(
'bulk_change_enrollment',
*args.split(' ')
) )
def test_without_commit(self): def test_without_commit(self):
...@@ -152,11 +166,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -152,11 +166,15 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
self._enroll_users(self.course, self.users, 'audit') self._enroll_users(self.course, self.users, 'audit')
CourseModeFactory(course_id=self.course.id, mode_slug='honor') CourseModeFactory(course_id=self.course.id, mode_slug='honor')
args = '--course {course} --from_mode {from_mode} --to_mode {to_mode}'.format(
course=text_type(self.course.id),
from_mode='audit',
to_mode='honor'
)
call_command( call_command(
'bulk_change_enrollment', 'bulk_change_enrollment',
course=unicode(self.course.id), *args.split(' ')
from_mode='audit',
to_mode='honor',
) )
# Verify that no users are in the honor mode. # Verify that no users are in the honor mode.
...@@ -170,7 +188,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -170,7 +188,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
with self.assertRaises(CommandError): with self.assertRaises(CommandError):
call_command( call_command(
'bulk_change_enrollment', 'bulk_change_enrollment',
course=unicode(self.course.id), course=text_type(self.course.id),
from_mode='audit', from_mode='audit',
) )
...@@ -180,7 +198,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -180,7 +198,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
command_options = { command_options = {
'from_mode': 'audit', 'from_mode': 'audit',
'to_mode': 'honor', 'to_mode': 'honor',
'course': unicode(self.course.id), 'course': text_type(self.course.id),
} }
command_options.pop(option) command_options.pop(option)
...@@ -209,7 +227,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -209,7 +227,7 @@ class BulkChangeEnrollmentTests(SharedModuleStoreTestCase):
[ [
call( call(
EVENT_NAME_ENROLLMENT_MODE_CHANGED, EVENT_NAME_ENROLLMENT_MODE_CHANGED,
{'course_id': unicode(course.id), 'user_id': user.id, 'mode': to_mode} {'course_id': text_type(course.id), 'user_id': user.id, 'mode': to_mode}
), ),
] ]
) )
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import ddt import ddt
from mock import patch from mock import patch
from six import text_type
from django.core.management import call_command from django.core.management import call_command
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
...@@ -53,13 +54,6 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -53,13 +54,6 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase):
""" The command should update the user's enrollment. """ """ The command should update the user's enrollment. """
user_str = ','.join([getattr(user, method) for user in self.users]) user_str = ','.join([getattr(user, method) for user in self.users])
user_ids = [u.id for u in self.users] user_ids = [u.id for u in self.users]
command_args = {
'course_id': unicode(self.course.id),
'to_mode': 'honor',
'from_mode': 'audit',
'noop': noop,
method: user_str,
}
# Verify users are not in honor mode yet # Verify users are not in honor mode yet
self.assertEqual( self.assertEqual(
...@@ -67,11 +61,19 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -67,11 +61,19 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase):
0 0
) )
call_command( noop = " --noop" if noop else ""
'change_enrollment',
**command_args # Hack around call_command bugs dealing with required options see:
# https://stackoverflow.com/questions/32036562/call-command-argument-is-required
command_args = '--course {course} --to honor --from audit --{method} {user_str}{noop}'.format(
course=text_type(self.course.id),
noop=noop,
method=method,
user_str=user_str
) )
call_command('change_enrollment', *command_args.split(' '))
# Verify correct number of users are now in honor mode # Verify correct number of users are now in honor mode
self.assertEqual( self.assertEqual(
len(CourseEnrollment.objects.filter(mode='honor', user_id__in=user_ids)), len(CourseEnrollment.objects.filter(mode='honor', user_id__in=user_ids)),
...@@ -95,12 +97,6 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -95,12 +97,6 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase):
all_users.append(fake_user) all_users.append(fake_user)
user_str = ','.join(all_users) user_str = ','.join(all_users)
real_user_ids = [u.id for u in self.users] real_user_ids = [u.id for u in self.users]
command_args = {
'course_id': unicode(self.course.id),
'to_mode': 'honor',
'from_mode': 'audit',
method: user_str,
}
# Verify users are not in honor mode yet # Verify users are not in honor mode yet
self.assertEqual( self.assertEqual(
...@@ -108,11 +104,14 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase): ...@@ -108,11 +104,14 @@ class ChangeEnrollmentTests(SharedModuleStoreTestCase):
0 0
) )
call_command( command_args = '--course {course} --to honor --from audit --{method} {user_str}'.format(
'change_enrollment', course=text_type(self.course.id),
**command_args method=method,
user_str=user_str
) )
call_command('change_enrollment', *command_args.split(' '))
# Verify correct number of users are now in honor mode # Verify correct number of users are now in honor mode
self.assertEqual( self.assertEqual(
len(CourseEnrollment.objects.filter(mode='honor', user_id__in=real_user_ids)), len(CourseEnrollment.objects.filter(mode='honor', user_id__in=real_user_ids)),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment