123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- import random
- import time
- from datetime import timedelta
- from django.contrib.auth import get_user_model
- from django.core.management.base import BaseCommand
- from django.utils import timezone
- from faker import Factory
- from ....categories.models import Category
- from ....core.pgutils import chunk_queryset
- from ....threads.checksums import update_post_checksum
- from ....threads.models import Thread
- from ....users.models import Rank
- from ...posts import get_fake_hidden_post, get_fake_post, get_fake_unapproved_post
- from ...threads import (
- get_fake_closed_thread,
- get_fake_hidden_thread,
- get_fake_thread,
- get_fake_unapproved_thread,
- )
- from ...users import (
- get_fake_admin_activated_user,
- get_fake_banned_user,
- get_fake_deleted_user,
- get_fake_inactive_user,
- get_fake_user,
- )
- User = get_user_model()
- class Command(BaseCommand):
- help = "Creates fake forum history reaching specified period."
- def add_arguments(self, parser):
- parser.add_argument(
- "length",
- help="generated history length (in days)",
- nargs="?",
- type=int,
- default=5,
- )
- parser.add_argument(
- "max_actions",
- help="number of items generate for a single day",
- nargs="?",
- type=int,
- default=50,
- )
- def handle(self, *args, **options): # pylint: disable=too-many-locals
- history_length = options["length"]
- max_actions = options["max_actions"]
- fake = Factory.create()
- categories = list(Category.objects.all_categories())
- ranks = list(Rank.objects.all())
- message = "Creating fake forum history for %s days...\n"
- self.stdout.write(message % history_length)
- start_time = time.time()
- self.move_existing_users_to_past(history_length)
- start_timestamp = timezone.now()
- for days_ago in reversed(range(history_length)):
- date = start_timestamp - timedelta(days=days_ago)
- for date_variation in get_random_date_variations(date, 0, max_actions):
- action = random.randint(0, 100)
- if action >= 80:
- self.create_fake_user(fake, date_variation, ranks)
- elif action > 50:
- self.create_fake_thread(fake, date_variation, categories)
- else:
- self.create_fake_post(fake, date_variation)
- if random.randint(0, 100) > 80:
- self.create_fake_follow(date)
- self.synchronize_threads()
- self.synchronize_categories()
- total_time = time.time() - start_time
- total_humanized = time.strftime("%H:%M:%S", time.gmtime(total_time))
- message = "\n\nSuccessfully created fake history for %s days in %s"
- self.stdout.write(message % (history_length, total_humanized))
- def move_existing_users_to_past(self, history_length):
- for user in User.objects.all():
- user.joined_on -= timedelta(days=history_length)
- user.save(update_fields=["joined_on"])
- user.audittrail_set.all().delete()
- def create_fake_user(self, fake, date, ranks):
- # Pick random rank for next user
- rank = random.choice(ranks)
- # There's 10% chance user is inactive
- if random.randint(0, 100) > 90:
- user = get_fake_inactive_user(fake, rank)
- # There's another 10% chance user is admin-activated
- elif random.randint(0, 100) > 90:
- user = get_fake_admin_activated_user(fake, rank)
- # And further chance user is banned
- elif random.randint(0, 100) > 90:
- user = get_fake_banned_user(fake, rank)
- # Or deleted their account
- elif random.randint(0, 100) > 90:
- user = get_fake_deleted_user(fake, rank)
- # User is active
- else:
- user = get_fake_user(fake, rank)
- user.joined_on = date
- user.save(update_fields=["joined_on"])
- user.audittrail_set.all().delete()
- self.write_event(date, "%s has joined" % user)
- def create_fake_thread(self, fake, date, categories):
- category = random.choice(categories)
- # 10% chance thread poster is anonymous
- if random.randint(0, 100) > 90:
- starter = None
- else:
- starter = self.get_random_user(date)
- # There's 10% chance thread is closed
- if random.randint(0, 100) > 90:
- thread = get_fake_closed_thread(fake, category, starter)
- # There's further 5% chance thread is hidden
- elif random.randint(0, 100) > 95:
- if random.randint(0, 100) > 90:
- hidden_by = None
- else:
- hidden_by = self.get_random_user(date)
- thread = get_fake_hidden_thread(fake, category, starter, hidden_by)
- # And further 5% chance thread is unapproved
- elif random.randint(0, 100) > 95:
- thread = get_fake_unapproved_thread(fake, category, starter)
- # Default, standard thread
- else:
- thread = get_fake_thread(fake, category, starter)
- thread.first_post.posted_on = date
- thread.first_post.updated_on = date
- thread.first_post.checksum = update_post_checksum(thread.first_post)
- thread.first_post.save(update_fields=["checksum", "posted_on", "updated_on"])
- thread.started_on = date
- thread.save(update_fields=["started_on"])
- self.write_event(
- date, '%s has started "%s" thread' % (thread.first_post.poster_name, thread)
- )
- def create_fake_post(self, fake, date):
- thread = self.get_random_thread(date)
- if not thread:
- return
- # 10% chance poster is anonymous
- if random.randint(0, 100) > 90:
- poster = None
- else:
- poster = self.get_random_user(date)
- # There's 5% chance post is unapproved
- if random.randint(0, 100) > 90:
- post = get_fake_unapproved_post(fake, thread, poster)
- # There's further 5% chance post is hidden
- elif random.randint(0, 100) > 95:
- if random.randint(0, 100) > 90:
- hidden_by = None
- else:
- hidden_by = self.get_random_user(date)
- post = get_fake_hidden_post(fake, thread, poster, hidden_by)
- # Default, standard post
- else:
- post = get_fake_post(fake, thread, poster)
- post.posted_on = date
- post.updated_on = date
- post.checksum = update_post_checksum(post)
- post.save(update_fields=["checksum", "posted_on", "updated_on"])
- self.write_event(
- date, '%s has replied to "%s" thread' % (post.poster_name, thread)
- )
- def create_fake_follow(self, date):
- user_a = self.get_random_user(date)
- user_b = self.get_random_user(date)
- if not (user_a or user_b) or user_a == user_b:
- return
- if not user_a.is_following(user_b):
- user_a.follows.add(user_b)
- self.write_event(date, "%s followed %s" % (user_a, user_b))
- def get_random_thread(self, date):
- return (
- Thread.objects.filter(started_on__lt=date)
- .select_related("category")
- .order_by("?")
- .first()
- )
- def get_random_user(self, date):
- return (
- User.objects.filter(
- joined_on__lt=date, requires_activation=User.ACTIVATION_NONE
- )
- .order_by("?")
- .first()
- )
- def write_event(self, date, event):
- formatted_date = date.strftime("%Y-%m-%d %H:%M")
- self.stdout.write("%s: %s" % (formatted_date, event))
- def synchronize_threads(self):
- self.stdout.write("\nSynchronizing threads...")
- start_time = time.time()
- for thread in chunk_queryset(Thread.objects.all()):
- thread.synchronize()
- thread.save()
- total_time = time.time() - start_time
- total_humanized = time.strftime("%H:%M:%S", time.gmtime(total_time))
- message = "Synchronized %s threads in %s"
- self.stdout.write(message % (Thread.objects.count(), total_humanized))
- def synchronize_categories(self):
- self.stdout.write("\nSynchronizing categories...")
- start_time = time.time()
- for category in Category.objects.all_categories():
- category.synchronize()
- category.save()
- total_time = time.time() - start_time
- total_humanized = time.strftime("%H:%M:%S", time.gmtime(total_time))
- message = "Synchronized %s categories in %s"
- self.stdout.write(message % (Category.objects.count(), total_humanized))
- def get_random_date_variations(date, min_date, max_date):
- variations = []
- for _ in range(random.randint(min_date, max_date)):
- random_offset = timedelta(minutes=random.randint(1, 1200))
- variations.append(date - random_offset)
- return sorted(variations)
|