From bdd5d0666fe1594ff32aa203b7cf405882bf3dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Sun, 19 Mar 2017 14:46:08 +0100 Subject: [PATCH] Added command to create example data. --- openslides/users/models.py | 4 +- tests/example_data_generator/README.rst | 13 + tests/example_data_generator/__init__.py | 1 + tests/example_data_generator/apps.py | 8 + .../management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/create-example-data.py | 226 ++++++++++++++++++ tests/unit/users/test_models.py | 2 +- 8 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 tests/example_data_generator/README.rst create mode 100644 tests/example_data_generator/__init__.py create mode 100644 tests/example_data_generator/apps.py create mode 100644 tests/example_data_generator/management/__init__.py create mode 100644 tests/example_data_generator/management/commands/__init__.py create mode 100644 tests/example_data_generator/management/commands/create-example-data.py diff --git a/openslides/users/models.py b/openslides/users/models.py index 70a5cf5e0..3ec29eed2 100644 --- a/openslides/users/models.py +++ b/openslides/users/models.py @@ -36,13 +36,13 @@ class UserManager(BaseUserManager): 'permissions', queryset=Permission.objects.select_related('content_type'))))) - def create_user(self, username, password, **kwargs): + def create_user(self, username, password, skip_autoupdate=False, **kwargs): """ Creates a new user only with a password and a username. """ user = self.model(username=username, **kwargs) user.set_password(password) - user.save(using=self._db) + user.save(skip_autoupdate=skip_autoupdate, using=self._db) return user def create_or_reset_admin_user(self): diff --git a/tests/example_data_generator/README.rst b/tests/example_data_generator/README.rst new file mode 100644 index 000000000..830cf3084 --- /dev/null +++ b/tests/example_data_generator/README.rst @@ -0,0 +1,13 @@ +Example Data Generator +====================== + +This is a plugin to provide a management command to generate example data +for OpenSlides. Add this module to your personal settings.py. + + INSTALLED_PLUGINS += ( + 'tests.example_data_generator', + ) + +Then run:: + + $ python manage.py create-example-data diff --git a/tests/example_data_generator/__init__.py b/tests/example_data_generator/__init__.py new file mode 100644 index 000000000..7254229ae --- /dev/null +++ b/tests/example_data_generator/__init__.py @@ -0,0 +1 @@ +default_app_config = 'tests.example_data_generator.apps.ExampleDataGeneratorAppConfig' diff --git a/tests/example_data_generator/apps.py b/tests/example_data_generator/apps.py new file mode 100644 index 000000000..9c740bb77 --- /dev/null +++ b/tests/example_data_generator/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class ExampleDataGeneratorAppConfig(AppConfig): + name = 'tests.example_data_generator' + label = 'tests.example_data_generator' + verbose_name = 'Example Data Generator' + version = 'no specific version' diff --git a/tests/example_data_generator/management/__init__.py b/tests/example_data_generator/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/example_data_generator/management/commands/__init__.py b/tests/example_data_generator/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/example_data_generator/management/commands/create-example-data.py b/tests/example_data_generator/management/commands/create-example-data.py new file mode 100644 index 000000000..d259c2f95 --- /dev/null +++ b/tests/example_data_generator/management/commands/create-example-data.py @@ -0,0 +1,226 @@ +from textwrap import dedent + +from django.contrib.auth.hashers import make_password +from django.core.management.base import BaseCommand, CommandError +from django.utils.crypto import get_random_string + +from openslides.agenda.models import Item +from openslides.assignments.models import Assignment +from openslides.motions.models import Motion +from openslides.topics.models import Topic +from openslides.users.models import Group, User + +MOTION_NUMBER_OF_PARAGRAPHS = 4 + +LOREM_IPSUM = [ + """\ +

Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod + tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim + veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea + commodi consequat. Quis aute iure reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat + cupiditat non proident, sunt in culpa qui officia deserunt mollit anim + id est laborum.

+ """, + + """\ +

Sed ut perspiciatis, unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae + ab illo inventore veritatis et quasi architecto beatae vitae dicta + sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, + aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui + dolorem ipsum, quia dolor sit amet consectetur adipisci[ng] velit, sed + quia non numquam [do] eius modi tempora inci[di]dunt, ut labore et + dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, + quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut + aliquid ex ea commodi consequatur? Quis autem vel eum iure + reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae + consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla + pariatur?

+ """, + + """\ +

At vero eos et accusamus et iusto odio dignissimos ducimus, qui + blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores + et quas molestias excepturi sint, obcaecati cupiditate non provident, + similique sunt in culpa, qui officia deserunt mollitia animi, id est + laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita + distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, + cumque nihil impedit, quo minus id, quod maxime placeat, facere + possimus, omnis voluptas assumenda est, omnis dolor repellendus. + Temporibus autem quibusdam et aut officiis debitis aut rerum + necessitatibus saepe eveniet, ut et voluptates repudiandae sint et + molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente + delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut + perferendis doloribus asperiores repellat…

+ """, +] + +DEFAULT_NUMBER = 100 +STAFF_USER_USERNAME = 'admin{}' +DEFAULT_USER_USERNAME = 'user{}' +PASSWORD = 'password' + + +class Command(BaseCommand): + """ + Command to create example data for OpenSlides. + """ + help = 'Create example data for OpenSlides.' + + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + + def add_arguments(self, parser): + """ + Adds arguments to the command parser. The default values for the apps + are set by DEFAULT_NUMBER. + """ + parser.add_argument( + '--only', + action='store_true', + help='Only the given objects are created i. e. all defaults are ' + 'set to 0.', + ) + parser.add_argument( + '-t', '--topics', + type=int, + help='Number of topics to be created (default {}).'.format( + DEFAULT_NUMBER), + ) + parser.add_argument( + '-m', '--motions', + type=int, + help='Number of motions to be created (default {}).'.format( + DEFAULT_NUMBER), + ) + parser.add_argument( + '-a', '--assignments', + type=int, + help='Number of assignments to be created (default {}).'.format( + DEFAULT_NUMBER), + ) + parser.add_argument( + '-u', '--users', + nargs=2, + type=int, + help='Number of users to be created. The first number of users is ' + 'added to the group "Staff" (default {}). The second number ' + 'of users is not added to any group (default {}).'.format( + DEFAULT_NUMBER, DEFAULT_NUMBER), + ) + + def handle(self, *args, **options): + self.create_topics(options) + self.create_motions(options) + self.create_assignments(options) + self.create_users(options) + + def create_topics(self, options): + number_of_topics = options['topics'] + if number_of_topics is None and not options['only']: + number_of_topics = DEFAULT_NUMBER + if number_of_topics is not None and number_of_topics > 0: + self.stdout.write('Start creating {} topcis ...'.format(number_of_topics)) + current_topics = list(Topic.objects.values_list('id', flat=True)) + new_topics = [] + for i in range(number_of_topics): + new_topics.append(Topic(title=get_random_string(20, self.chars))) + Topic.objects.bulk_create(new_topics) + items = [] + for topic in Topic.objects.exclude(pk__in=current_topics): + items.append(Item(content_object=topic, type=Item.AGENDA_ITEM)) + Item.objects.bulk_create(items) + self.stdout.write(self.style.SUCCESS('{} topcis successfully created.'.format(number_of_topics))) + elif number_of_topics is not None and number_of_topics < 0: + raise CommandError('Number for topics must not be negative.') + + def create_motions(self, options): + number_of_motions = options['motions'] + if number_of_motions is None and not options['only']: + number_of_motions = DEFAULT_NUMBER + if number_of_motions is not None and number_of_motions > 0: + self.stdout.write('Start creating {} motions ... (sorry, this might be slow) ...'.format(number_of_motions)) + text = '' + for i in range(MOTION_NUMBER_OF_PARAGRAPHS): + text += dedent(LOREM_IPSUM[i % 3]) + for i in range(number_of_motions): + motion = Motion( + title=get_random_string(20, self.chars), + text=text, + ) + motion.save(skip_autoupdate=True) + self.stdout.write(self.style.SUCCESS('{} motions successfully created.'.format(number_of_motions))) + elif number_of_motions is not None and number_of_motions < 0: + raise CommandError('Number for motions must not be negative.') + + def create_assignments(self, options): + number_of_assignments = options['assignments'] + if number_of_assignments is None and not options['only']: + number_of_assignments = DEFAULT_NUMBER + if number_of_assignments is not None and number_of_assignments > 0: + self.stdout.write('Start creating {} assignments ...'.format(number_of_assignments)) + current_assignments = list(Assignment.objects.values_list('id', flat=True)) + new_assignments = [] + for i in range(number_of_assignments): + new_assignments.append(Assignment(title=get_random_string(20, self.chars), open_posts=1)) + Assignment.objects.bulk_create(new_assignments) + items = [] + for assignment in Assignment.objects.exclude(pk__in=current_assignments): + items.append(Item(content_object=assignment)) + Item.objects.bulk_create(items) + self.stdout.write(self.style.SUCCESS('{} assignments successfully created.'.format(number_of_assignments))) + elif number_of_assignments is not None and number_of_assignments < 0: + raise CommandError('Number for assignments must not be negative.') + + def create_users(self, options): + self.create_staff_users(options) + self.create_default_users(options) + + def create_staff_users(self, options): + if options['users'] is None and not options['only']: + staff_users = DEFAULT_NUMBER + elif options['users'] is None: + staff_users = None + else: + staff_users = options['users'][0] + if staff_users is not None and staff_users > 0: + self.stdout.write('Start creating {} staff users ... (sorry, this might be slow) ...'.format(staff_users)) + group_staff = Group.objects.get(name='Staff') + hashed_password = make_password(PASSWORD, '', 'md5') + current_users = list(User.objects.values_list('id', flat=True)) + new_users = [] + for i in range(staff_users): + new_users.append(User( + username=STAFF_USER_USERNAME.format(i), + default_password=PASSWORD, + password=hashed_password + )) + User.objects.bulk_create(new_users) + for user in User.objects.exclude(pk__in=current_users): + user.groups.add(group_staff) + self.stdout.write(self.style.SUCCESS('{} staff users successfully created.'.format(staff_users))) + elif staff_users is not None and staff_users < 0: + raise CommandError('Number for staff users must not be negative.') + + def create_default_users(self, options): + if options['users'] is None and not options['only']: + default_users = DEFAULT_NUMBER + elif options['users'] is None: + default_users = None + else: + default_users = options['users'][1] + if default_users is not None and default_users > 0: + self.stdout.write('Start creating {} default users ...'.format(default_users)) + hashed_password = make_password(PASSWORD, '', 'md5') + new_users = [] + for i in range(default_users): + new_users.append(User( + username=DEFAULT_USER_USERNAME.format(i), + default_password=PASSWORD, + password=hashed_password + )) + User.objects.bulk_create(new_users) + self.stdout.write(self.style.SUCCESS('{} default users successfully created.'.format(default_users))) + elif default_users is not None and default_users < 0: + raise CommandError('Number for default users must not be negative.') diff --git a/tests/unit/users/test_models.py b/tests/unit/users/test_models.py index adc46ba6b..0d640fec5 100644 --- a/tests/unit/users/test_models.py +++ b/tests/unit/users/test_models.py @@ -18,7 +18,7 @@ class UserManagerTest(TestCase): user_manager.model.assert_called_once_with(username='test_username', test_kwarg='test_kwarg') user.set_password.assert_called_once_with('test_password') - user.save.assert_called_once_with(using='my_test_db') + user.save.assert_called_once_with(using='my_test_db', skip_autoupdate=False) self.assertEqual( return_user, user,