root/trunk/tools/populate

Revision 4196, 22.0 kB (checked in by takayama, 3 months ago)

* Set svn keywords

  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl
2
3# FIXME: authenticated commenters aren't created
4
5use strict;
6use warnings;
7
8use lib 'extlib', 'lib';
9use Data::Dumper;
10use Getopt::Long;
11use MT;
12use MT::Author;
13use MT::Permission;
14
15my $mt = MT->new() or die "No MT object " . MT->errstr();
16
17my $blogs   = 1;
18my $destroy = 0;
19my $authors = 3;
20my $entries = 500;
21my $cats    = 5;
22# this can be used to scale the default amount of data generated
23my $weight_factor = 1;
24my $bypass_confirm = 0;
25my $help    = 0;
26my $silent  = 0;
27my $years   = 1;
28
29my $result = GetOptions("authors=i"    => \$authors,
30                        "blogs=i"      => \$blogs,
31                        "categories=i" => \$cats,
32                        "entries=i"    => \$entries,
33                        "weight=i"     => \$weight_factor,
34                        "destroy"      => \$destroy,
35                        "yes-really"   => \$bypass_confirm,
36                        "help|?"       => \$help,
37                        "silent"       => \$silent,
38                        "years=i"      => \$years,
39);
40
41if ($help) {
42    eval { require Pod::Usage };
43    die "No Pod::Usage available. Use the Source, Luke." if $@;
44    Pod::Usage::pod2usage(-exitstatus => 1);
45}
46
47use vars qw($this_blog $this_iter $this_obj $this_parent $tmpl_list);
48
49# define objects and such...
50use constant RAND_BOOL => [ 0, 1 ];
51
52my @word_pool = qw(
53    cool tech blogging politics interesting video
54    perl python ruby web2.0 sixapart reference link audio
55    php javascript voip news
56);
57
58# Randomize the random number generator to make things nice and consistent.
59srand(1);
60
61# these are all the numbers that control how many of what are generated
62my $blog_count             = sub { $blogs              * $weight_factor };
63my $authors_per_blog       = sub { $authors            * $weight_factor };
64my $categories_per_blog    = sub { $cats + int(rand(10)) * $weight_factor };
65my $entries_per_blog       = sub { $entries + int(rand(30)) * $weight_factor };
66my $notifications_per_blog = sub { 3   + int(rand(20))                  };
67my $bans_per_blog          = sub { 2   + int(rand(10))                  };
68my $comments_per_entry     = sub {       int(rand(10)) * $weight_factor };
69my $tags_per_entry         = sub {       int(rand(5))  * $weight_factor };
70my $pings_per_trackback    = sub {       int(rand(10)) * $weight_factor };
71my %defs = (
72    'MT::Blog' => {
73        table => 'blog',
74        as_of => 1.0,
75        needs => [
76            'MT::Author'       => $authors_per_blog,
77            'MT::Category'     => $categories_per_blog,
78            'MT::Entry'        => $entries_per_blog,
79            'MT::Template'     => \&template_loader,
80            'MT::Notification' => $notifications_per_blog,
81            'MT::IPBanList'    => $bans_per_blog,
82        ],
83        fields => [
84            field('name', \&random_blog_name),
85            field('description', sub { random_paragraph(int(rand(1))) } ),
86            field('archive_type', [ 'Individual,Category,Monthly',
87                'Individual,Monthly', 'Daily,Category,Monthly' ]),
88            field('archive_type_preferred', sub {
89                my @at = split /,/, $this_obj->archive_type;
90                $at[int(rand(scalar @at))]
91            } ),
92            field('site_path', sub { "/www/blog_$this_iter" } ),
93            field('archive_path', sub { $this_obj->site_path . '/archives' }),
94            field('site_url', sub { "http://www.blog${this_iter}.com/" } ),
95            field('archive_url', sub { $this_obj->site_url . 'archives/' }),
96            field('days_on_index', [ 7, 10, 14 ]),
97            field('entries_on_index', [ 10, 15, 20 ], 3.2),
98            field('file_extension', [ '', '', 'html', 'asp', 'jsp', 'php' ] ),
99            field('email_new_comments', RAND_BOOL),
100            field('allow_comment_html', RAND_BOOL),
101            field('autolink_urls', RAND_BOOL),
102            field('server_offset', [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ]),
103            field('ping_blogs', RAND_BOOL, 2.5),
104            field('ping_others', RAND_BOOL, 2.5),
105            field('autodiscover_links', RAND_BOOL, 2.5),
106            field('convert_paras', RAND_BOOL),
107            field('convert_paras_comments', RAND_BOOL),
108            field('sanitize_spec', [ 'a href,br/,p', '' ], 2.6),
109            field('cc_license', sub {
110                if (MT->version_number < 3.16) {
111                    [ '', 'by-nc-sa', 'nd', 'by', 'pd', 'sa',
112                      'by-nd-nc', 'by-nc' ];
113                } else {
114                    [ '', '' ], # FIXME: URLs for new cc licenses
115                }
116            }, 2.6),
117            field('is_dynamic', 0, 2.6),
118            field('require_comment_emails', RAND_BOOL, 3.0),
119            field('allow_unreg_comments', RAND_BOOL, 3.0),
120            field('allow_reg_comments', RAND_BOOL, 3.0),
121            field('manual_approve_commenters', RAND_BOOL, 3.0),
122            field('old_style_archive_links', RAND_BOOL, 3.0),
123            field('moderate_unreg_comments', RAND_BOOL, 3.0),
124            field('remote_auth_token', [ '', 'some_remote_token' ], 3.0),
125            field('ping_technorati', RAND_BOOL, 3.1),
126            field('children_modified_on', undef, 3.1),
127            field('custom_dynamic_templates',
128                [ 'none', 'archives', 'custom' ], 3.1),
129        ],
130    },
131    'MT::Entry' => {
132        table => 'entry',
133        as_of => 1.0,
134        needs => [
135            'MT::Comment' => sub { $this_obj->allow_comments ? $comments_per_entry->() : 0 },
136            'MT::Trackback' => sub { $this_obj->allow_pings ? 1 : 0 },
137            #'MT::Placement' => [ 0, 1, 1, 1, 2 ],
138        ],
139        fields => [
140            field('blog_id', sub { $this_blog->id } ),
141            field('author_id', \&random_author),
142            field('title', \&random_title),
143            field('text', sub { random_paragraph(2+int(rand(3)))} ),
144            field('text_more', sub { random_paragraph(int(rand(10))) } ),
145            field('excerpt', ''),
146            field('status', [ 1, 2, 2, 2, 2, 4 ]),
147            field('keywords', '', 2.5),
148            field('tangent_cache', '', 2.5),
149            field('allow_pings', [ 0, 1 ]),
150            field('allow_comments', [ 0, 1, 1, 1 ]),
151            field('convert_breaks', sub {
152                MT->version_number < 2.6 ? [ 0, 1 ] : [ '__default__', '0' ]
153            }),
154            field('created_on', \&random_date, 1.0 ),
155            field('basename', undef, 3.0), # auto-set upon save
156            field('tags', \&assign_random_tags, 3.3 ),
157            field('categories', \&assign_random_categories, 1.0 ),
158        ],
159    },
160    'MT::Author' => {
161        table => 'author',
162        as_of => 1.0,
163        needs => [
164            'MT::Permission' => \&permission_check,
165        ],
166        fields => [
167            field('name', \&random_username),
168            field('nickname', \&random_name),
169            field('email', \&random_email),
170            field('url', \&random_url),
171            field('type', sub { ref $this_parent eq 'MT::Comment' ? 2 : 1 }, 3.0),
172            field('password', sub { $this_obj->type == 1 ? crypt('Nelson', 'xx') : '' }),
173            field('preferred_language', sub { $this_obj->type == 1 ? ($this_obj->name eq 'Melody' ? 'en_US' : [ 'en_US', 'en_US', 'en-us', 'fr', 'de' ]) : undef }, 2.5),
174            field('can_create_blog', sub { $this_obj->type == 1 ? RAND_BOOL : undef }),
175            field('is_superuser', sub { $this_obj->type == 1 && $this_iter == 1 ? 1 : undef }, 3.2),
176            field('can_view_log', sub { $this_obj->type == 1 ? RAND_BOOL : undef }),
177            field('hint', sub { $this_obj->type == 1 ? 'hint' : undef } ),
178            field('public_key', undef),
179            field('api_password', sub { $this_obj->type == 1 ? 'Nelson' : undef }, 3.2),
180            field('remote_auth_username', undef, 3.0),
181            field('remote_auth_token', undef, 3.0),
182        ],
183    },
184    'MT::Category' => {
185        table => 'category',
186        as_of => 1.0,
187        needs => [
188            'MT::Trackback' => [ 0, 0, 0, 0, 0, 1 ],
189        ],
190        fields => [
191            field('blog_id', sub { $this_blog->id }),
192            field('label', \&random_category),
193            field('author_id', ),
194            field('description', sub { random_paragraph(int(rand(1))) }),
195            field('allow_pings', [ 0, 0, 0, 0, 1 ], 1.0), # FIXME: version #
196            field('parent', [ 0, 0, 0, \&random_category_id ], 3.1),
197        ],
198    },
199    'MT::Permission' => {
200        table => 'permission',
201        as_of => 1.0,
202        fields => [
203            field('author_id', sub { $this_parent->id } ),
204            field('blog_id', sub { $this_blog->id } ),
205            field('permissions', sub { $this_parent->type == 1 ? ($this_iter == 1 ? 14334 : &random_role ) : [ 0, 1 ] } ),
206            field('entry_prefs', undef),
207        ],
208    },
209    'MT::Comment' => {
210        table => 'comment',
211        as_of => 1.0,
212        needs => [
213            'MT::Author' => [ 0, 0, 0, 0, 1 ],
214        ],
215        fields => [
216            field('blog_id', sub { $this_parent->blog_id }),
217            field('entry_id', sub { $this_parent->id }),
218            field('author', \&random_name),
219            field('commenter_id', \&random_commenter, 3.0),
220            field('email', \&random_email),
221            field('url', \&random_url),
222            field('text', sub { random_paragraph(1+int(rand(2))) } ),
223            field('ip', \&random_ip),
224            field('visible', [ 0, 1, 1, 1, 1 ], 3.0),
225            field('junk_status', [ -1, 1, 1, 1, 1 ], 3.2),
226            field('junk_score', sub { $this_obj->junk_status == -1 ? rand(2) * -1 : 0 }, 3.2),
227            field('junk_log', sub { $this_obj->junk_status == -1 ? 'Random (' . $this_obj->junk_score . "): Randomly scored as junk\n" : '' }, 3.2),
228        ],
229    },
230    'MT::Trackback' => {
231        table => 'trackback',
232        as_of => 1.2,   # ??
233        needs => [
234            'MT::TBPing' => $pings_per_trackback,
235        ],
236        fields => [
237            field('blog_id', sub { $this_parent->blog_id }),
238            field('title', \&random_title),
239            field('description', undef),
240            field('rss_file', undef),
241            field('url', undef),
242            field('entry_id', sub { ref $this_parent eq 'MT::Entry' ? $this_parent->id : 0 }),
243            field('category_id', sub { ref $this_parent eq 'MT::Category' ? $this_parent->id : 0 }),
244            field('is_disabled', 0),
245            field('passphrase', undef),
246        ],
247    },
248    'MT::TBPing' => {
249        table => 'tbping',
250        as_of => 1.2,   # ??
251        fields => [
252            field('blog_id', sub { $this_parent->blog_id }),
253            field('tb_id', sub { $this_parent->id }),
254            field('title', \&random_title),
255            field('excerpt', \&random_paragraph ),
256            field('source_url', \&random_url),
257            field('ip', \&random_ip),
258            field('blog_name', \&random_blog_name),
259            field('visible', [ 0, 1, 1, 1, 1 ], 3.0),
260            field('junk_status', [ -1, 1, 1, 1, 1 ], 3.2),
261            field('junk_score', sub { $this_obj->junk_status == -1 ? rand(2) * -1 : 0 }, 3.2),
262            field('junk_log', sub { $this_obj->junk_status == -1 ? 'Random (' . $this_obj->junk_score . "): Randomly scored as junk\n" : '' }, 3.2),
263        ],
264    },
265    'MT::Template' => {
266        table => 'template',
267        as_of => 1.0,
268#        fields => [
269#            field('build_dynamic', [], 3.1),
270#        ],
271    },
272    'MT::TemplateMap' => {
273        table => 'templatemap',
274        as_of => 2.0,
275    },
276    'MT::IPBanList' => {
277        table => 'ipbanlist',
278        as_of => 1.4,  # ??
279        fields => [
280            field('blog_id', sub { $this_parent->id } ),
281            field('ip', \&random_ip),
282        ],
283    },
284    'MT::Log' => {
285        table => 'log',
286        as_of => 1.0,
287#        fields => [
288#            field('blog_id', [], 3.2),
289#        ],
290    },
291    'MT::Session' => {
292        table => 'session',
293        as_of => 3.0,
294    },
295    'MT::Notification' => {
296        table => 'notification',
297        as_of => 1.0,
298        fields => [
299            field('blog_id', sub { $this_parent->id } ),
300            field('name', \&random_name),
301            field('email', \&random_email),
302            field('url', \&random_url),
303        ],
304    },
305    'MT::Placement' => {
306        table => 'placement',
307        as_of => 2.0,
308    },
309    'MT::FileInfo' => {
310        table => 'fileinfo',
311        as_of => 3.1,
312    },
313    'MT::PluginData' => {
314        table => 'plugindata',
315        as_of => 2.6,
316    },
317    'MT::Tag' => {
318        table => 'tag',
319        as_of => 3.3,
320    },
321    'MT::ObjectTag' => {
322        table => 'objecttag',
323        as_of => 3.3,
324    },
325#    'MT::Config' => {
326#        table => 'config',
327#        as_of => 3.2,
328#    },
329);
330
331if ($destroy) {
332    unless ($bypass_confirm) {
333        print <<'WARNING';
334** WARNING **
335
336You've chosen to clear all existing data in your database and populate
337it with random, generated data. The system administrator login will be
338Melody/Nelson. This tool is mainly used for testing MT performance and
339usability with varying sizes of data.
340
341To continue, type OK and press enter.
342WARNING
343
344        my $confirm = <STDIN>;
345        chomp $confirm;
346        exit unless $confirm eq 'OK';
347    }
348    clean();
349}
350
351# kick off the build process
352build( 'MT::Blog', $blog_count->(), 0);
353
354sub field {
355    my ($name, $inputs, $as_of) = @_;
356    my $def = { inputs => $inputs };
357    $def->{as_of} = $as_of if defined $as_of;
358    ( $name, $def );
359}
360
361sub clean {
362    # CLEAR ANY EXISTING DATA
363
364    print "Clearing existing data...\n" unless $silent;
365    my $driver = MT::Object->driver();
366    if (my $dbh = $driver->rw_handle) {
367        my @tables = map { $defs{$_}{table} } keys %defs;
368        foreach (@tables) {
369            print "\tWiping $_\n" unless $silent;
370            $dbh->do("delete from mt_$_");
371        }
372        if ($driver->isa('MT::ObjectDriver::Driver::DBD::Pg')) {
373            # reset sequences
374            $dbh->do("drop sequence mt_${_}_id") foreach @tables;
375            $dbh->do("create sequence mt_${_}_id") foreach @tables;
376        }
377    }
378}
379
380sub build {
381    my ($pkg, $num, $depth) = @_;
382
383    my $def = $defs{$pkg};
384
385    return unless $def->{as_of} <= MT->version_number();
386
387    print(("\t" x $depth) . "Creating $num $pkg objects...\n") unless $silent;
388
389    local $this_obj = $this_obj;
390    local $this_parent = $this_obj;
391    local $this_iter;
392    for (my $i = 0; $i < $num; $i++) {
393        $this_iter = $i + 1;  # always indexed from 1...
394
395        my $obj;
396        # special case for MT::Author...
397        if ($pkg eq 'MT::Author') {
398            my $name = random_username();
399            my $user = MT::Author->load({name => $name, type => 1});
400            if ($user) {
401                $obj = $user;
402            }
403        }
404        unless ($obj) {
405            no strict 'refs';
406            no warnings;
407            eval("require $pkg") unless defined *{"$pkg::"};
408            $obj = $pkg->new();
409        }
410        $this_obj = $obj;
411        if (!$obj->id) {
412            my @fields = @{$def->{fields}};
413            while (my ($fld, $fld_def) = (shift @fields, shift @fields)) {
414                if ((!exists $fld_def->{as_of}) || ($fld_def->{as_of} && ($fld_def->{as_of} <= $MT::VERSION))) {
415                    my $input = $fld_def->{inputs};
416                    while (ref $input) {
417                        if (ref $input eq 'ARRAY') {
418                            $input = $input->[int(rand(scalar @$input))];
419                        }
420                        if (ref $input eq 'CODE') {
421                            $input = $input->();
422                        }
423                    }
424                    print "\tsetting $fld to [$input]\n" if !$silent && defined $input;
425                    $obj->$fld($input) if defined $input;
426                }
427                last unless @fields;
428            }
429            $obj->save or die $obj->errstr;
430        }
431
432        if ($pkg eq 'MT::Blog') {
433            $this_blog = $obj; # assign for use in other objects...
434        }
435
436        # process needs...
437        if (my $needs = $def->{needs}) {
438            my @needs = @$needs;
439            while (my ($pkg, $num) = (shift @needs, shift @needs)) {
440                while (ref $num) {
441                    if (ref $num eq 'ARRAY') {
442                        $num = $num->[int(rand(scalar @$num))];
443                    }
444                    if (ref $num eq 'CODE') {
445                        $num = $num->();
446                    }
447                }
448                if ($num) {
449                    build($pkg, $num, $depth+1);
450                }
451                last unless @needs;
452            }
453        }
454    }
455}
456
457sub permission_check {
458    my $author = $this_obj;
459    my $blog = $this_blog;
460    require MT::Permission;
461    my $num = MT::Permission->count({ author_id => $author->id,
462        blog_id => $blog->id });
463    $num ? 0 : 1;
464}
465
466sub template_loader {
467    my $blog = $this_blog;
468    print "\tCreating templates...\n" unless $silent;
469    $blog->create_default_templates();
470    0;
471}
472
473## some random routines
474
475sub random_name {
476    $this_iter == 1 ? 'Melody Nelson' : 'Joe ' . $this_iter;
477}
478
479sub random_email {
480    "user" . $this_iter . '@gmail.com';
481}
482
483sub random_paragraph {
484    my $num = shift;
485    $num ||= 1 unless defined $num;
486    ("Lorem ipsum blah blah blah.\n\n" x $num) || '';
487}
488
489sub random_username {
490    $this_iter == 1 ? 'Melody' : 'user' . $this_iter;
491}
492
493sub random_blog_name {
494    'Weblog ' . $this_iter;
495}
496
497sub random_category_id {
498}
499
500sub random_category {
501    'Category ' . $this_iter;
502}
503
504sub random_title {
505    'Title for ' . (ref $this_obj) . ' #' . $this_iter;
506}
507
508sub random_commenter {
509    my @c = MT::Author->load({ type => 2 });
510    return unless @c;
511    my $c = $c[int(rand(scalar @c))];
512    $c->id;
513}
514
515sub random_author {
516    my @a = MT::Author->load({ type => 1 });
517    my $a = $a[int(rand(scalar @a))];
518    $a->id;
519}
520
521sub random_role {
522#        [ 1, 'comment', 'Post Comments', ],
523#        [ 4096, 'administer_blog', 'Blog Administrator'],
524#        [ 2, 'post', 'Post', ],
525#        [ 4, 'upload', 'Upload File', ],
526#        [ 8, 'edit_all_posts', 'Edit All Posts', ],
527#        [ 16, 'edit_templates', 'Manage Templates', ],
528##       [ 32, 'edit_authors', 'Edit Authors & Permissions', ],
529#        [ 64, 'edit_config', 'Configure Weblog', ],
530#        [ 128, 'rebuild', 'Rebuild Files', ],
531#        [ 256, 'send_notifications', 'Send Notifications', ],
532#        [ 512, 'edit_categories', 'Manage Categories', ],
533#        [ 1024, 'edit_notifications', 'Manage Notification List' ],
534#        [ 2048, 'not_comment', ''],  # not a real permission but a denial thereeof
535#        [ 8192, 'view_blog_log', 'View Activity Log For This Weblog']
536    my @roles;
537    if ($this_parent->type == 2) {
538        @roles = ("'comment'", "'comment'", "'comment'", "'comment'", "'not_comment'");
539    } else {
540        if (MT->version_number >= 3.2) {
541            push @roles, ("'post'", "'post'", "'post'", "'post'");
542            push @roles, ("'post','upload'");
543            push @roles, ("'post','upload','edit_all_posts'");
544            push @roles, ("'edit_templates','edit_all_posts','post','upload'");
545            push @roles, ("'edit_config','edit_templates','edit_all_posts','post','upload'");
546            push @roles, ("'edit_categories','edit_config','edit_templates','edit_all_posts','post','upload'");
547            push @roles, ("'view_blog_log','edit_config'");
548        } else {
549            push @roles, ("'post'", "'post'", "'post'", "'post'");
550            push @roles, ("'post','upload'");
551            push @roles, ("'edit_all_posts','post','upload'");
552            push @roles, ("'edit_templates','edit_all_posts','post','upload'");
553            push @roles, ("'edit_config','edit_templates','edit_all_posts','post','upload'");
554            push @roles, ("'edit_categories','edit_templates','edit_all_posts','post','upload'");
555            push @roles, ("'edit_config','edit_authors','edit_all_posts'");
556        }
557    }
558    $roles[int(rand(scalar @roles))];
559}
560
561sub random_url {
562    'http://www.example.com/user' . $this_iter . '/';
563}
564
565sub random_ip {
566    "192.168.0." . $this_iter;
567}
568
569sub assign_random_tags {
570    my @tags;
571    my $count = $tags_per_entry->();
572    for (my $i = 0; $i < $count; $i++) {
573        push @tags, $word_pool[rand(scalar @word_pool)];
574    }
575    $this_obj->tags(@tags);
576    undef;
577}
578
579sub assign_random_categories {
580    my @count = (0, 1, 1, 1, 1, 2);
581
582    # this is the last field; go ahead and save this object so
583    # we have an entry id.
584    $this_obj->save;
585
586    my $count = $count[int(rand(scalar @count))];
587    require MT::Category;
588    require MT::Placement;
589    my @cats = MT::Category->load({ blog_id => $this_obj->blog_id });
590    my %used;
591    $count = scalar @cats if scalar @cats < $count;
592    for (my $i = 0; $i < $count; $i++) {
593        my $cat = $cats[int(rand(scalar @cats))];
594        next if $used{$cat->id};
595        my $place = MT::Placement->new;
596        $place->category_id($cat->id);
597        $place->blog_id($this_obj->blog_id);
598        $place->entry_id($this_obj->id);
599        $place->is_primary(%used ? 0 : 1);
600        $place->save or die $place->errstr;
601        $used{$cat->id} = 1;
602    }
603    undef;
604}
605
606sub random_date {
607    my @date = localtime(time - int(rand($years * 365 * 24 * 60 * 60)));
608    return sprintf("%04d%02d%02d%02d%02d%02d",
609        $date[5] + 1900, $date[4] + 1, $date[3], $date[2], $date[1], $date[0]
610    );
611}
612
6131;
614
615__END__
616
617=head1 NAME
618
619populate - A utility for creating pseudorandom weblog data.
620
621=head1 SYNOPSIS
622
623    # Create 3 'random' weblogs with ~500 entries each
624    tools/populate --blogs 3 --destroy --yes-really
625
626    # Adds an addtional weblog
627    tools/populate --blogs 1
628
629=head1 OPTIONS
630           
631=over 4
632
633=item B<--destroy>
634
635Wipes your existing database before populating it. This switch is confirmed
636before execution.  If this is done, the system admin becomes Melody Nelson.
637
638=item --yes-really
639
640Bypasses the confirmation for the '--destroy' switch.
641
642=item --blogs E<lt>nE<gt>
643
644Lets you specify the number of weblogs to create (default is 1).
645
646=item --authors E<lt>nE<gt>
647
648Specifies the number of user records to create (default is 3).
649
650=item --entries E<lt>nE<gt>
651
652Specifies the number of entries (approximately) to create (default is 500).
653
654=item --categories E<lt>nE<gt>
655
656Specifies the number of categories (approximately) to create (default is 5).
657
658=item --years E<lt>nE<gt>
659
660Specifies the number of years to post-date entry creation (default is 1).
661
662=back
Note: See TracBrowser for help on using the browser.