New command 'new_story' in bin/gruta.
[gruta.git] / bin / gruta
blob8b60ca71ddab2e13b5f9ce157d3b75d47437f465
1 #!/usr/bin/perl
3 use strict;
4 use warnings;
6 use File::Temp;
8 use Gruta;
9 use Gruta::Source::DBI;
10 use Gruta::Source::FS;
11 use Gruta::Source::Mbox;
13 my $X = {
14 'copy' => [
15 'copy {src} {dst}',
16 'Copies the full source {src} to {dst}',
17 sub {
18 my $g = init();
19 my $new_src = arg();
20 my $dst = new_source( $new_src );
22 $dst->create();
24 $g->transfer_to_source( $dst );
27 'topics' => [
28 'topics {src}',
29 'Lists the topics in {src}',
30 sub {
31 my $g = init();
33 foreach my $t ($g->topics()) {
34 print $t, "\n";
38 'topic' => [
39 'topic {src} {topic_id}',
40 'Dumps topic data',
41 sub {
42 my $g = init();
43 my $topic_id = arg();
45 print get_topic($g, $topic_id);
48 'edit_topic' => [
49 'edit_topic {src} {topic_id}',
50 'Edits topic data',
51 sub {
52 my $g = init();
53 my $topic_id = arg();
55 my $fh = File::Temp->new();
56 print $fh get_topic($g, $topic_id);
57 my $fn = $fh->filename();
58 $fh->close();
60 my $mtime = (stat($fn))[9];
61 system('$EDITOR ' . $fn);
63 if ($mtime != (stat($fn))[9]) {
64 save_topic($g, $topic_id, $fn);
69 'stories' => [
70 'stories {src} {topic_id}',
71 'Lists all stories of a topic',
72 sub {
73 my $g = init();
74 my $topic_id = arg();
76 foreach my $s ($g->stories($topic_id)) {
77 print $s, "\n";
81 'story' => [
82 'story {src} {topic_id} {id}',
83 'Dumps story data',
84 sub {
85 my $g = init();
86 my $topic_id = arg();
87 my $id = arg();
89 print get_story($g, $topic_id, $id);
92 'edit_story' => [
93 'edit_story {src} {topic_id} {id}',
94 'Calls $EDITOR to edit story data',
95 sub {
96 my $g = init();
97 my $topic_id = arg();
98 my $id = arg();
100 my $fh = File::Temp->new();
101 print $fh get_story($g, $topic_id, $id);
102 my $fn = $fh->filename();
103 $fh->close();
105 my $mtime = (stat($fn))[9];
106 system('$EDITOR ' . $fn);
108 if ($mtime != (stat($fn))[9]) {
109 save_story($g, $topic_id, $id, $fn);
113 'new_story' => [
114 'new_story {src} {topic_id} {id} {date} {date2} {format} {tags}',
115 'Creates a new story from STDIN',
116 sub {
117 my $g = init();
118 my $topic_id = arg();
119 my $id = arg();
120 my $date = arg();
121 my $date2 = arg();
122 my $format = arg();
123 my $tags = arg();
125 my $content = join('', <>);
127 my $story = Gruta::Data::Story->new(
128 topic_id => $topic_id,
129 id => $id
132 $story->set('date', $date || Gruta::Data::today());
133 $story->set('date2', $date2);
134 $story->set('format', $format || 'grutatxt');
135 $story->set('ctime', time());
136 $story->set('userid', $ENV{USER});
137 $story->set('content', $content);
139 $story = $g->insert_story($story);
141 if ($tags) {
142 $story->tags(split(/\s*,\s*/, $tags));
146 'filter_story' => [
147 'filter_story {src} {topic_id} {id} {command}',
148 'Filters story data through command (STDIN, STDOUT)',
149 sub {
150 my $g = init();
151 my $topic_id = arg();
152 my $id = arg();
153 my $filter_cmd = arg();
155 my $fhr = File::Temp->new();
156 print $fhr get_story($g, $topic_id, $id);
157 my $fnr = $fhr->filename();
158 $fhr->close();
160 my $fhw = File::Temp->new();
161 my $fnw = $fhw->filename();
162 $fhw->close();
164 system("$filter_cmd < $fnr > $fnw");
166 save_story($g, $topic_id, $id, $fnw);
169 'create' => [
170 'create {src}',
171 'Creates {src}',
172 sub {
173 init();
176 'tags' => [
177 'tags {src}',
178 'Lists all tags in {src}',
179 sub {
180 my $g = init();
182 foreach my $t ($g->tags()) {
183 print join(' ', @{$t}), "\n";
187 'stories_by_date' => [
188 'stories_by_date {src} {topic(s)} {num} {offset} [{from}] [{to}] [{future}]',
189 'Searches stories by date',
190 sub {
191 my $g = init();
192 my $topics = arg();
193 my $num = arg();
194 my $offset = arg();
195 my $from = arg_o();
196 my $to = arg_o();
197 my $future = arg_o();
199 if ($topics) {
200 $topics = [ split(':', $topics) ];
203 foreach my $s ($g->stories_by_date(
204 $topics,
205 num => $num,
206 offset => $offset,
207 from => $from,
208 to => $to,
209 future => $future
210 ) ) {
211 print join(' ', @{$s}), "\n";
215 'stats' => [
216 'stats {src}',
217 'Dumps statistics for {src}',
218 sub {
219 my $g = init();
220 my $n_topics = 0;
221 my $n_stories = 0;
222 my $n_hits = 0;
224 foreach my $t ($g->topics()) {
225 $n_topics++;
227 foreach my $s ($g->stories($t)) {
228 $n_stories++;
230 my $story = $g->story($t, $s);
232 $n_hits += $story->get('hits') || 0;
236 print "Topics: $n_topics, Stories: $n_stories, Hits: $n_hits\n";
239 'stories_by_tag' => [
240 'stories_by_tag {src} {topic(s)} {tag(s)}',
241 'Searches stories by tag(s)',
242 sub {
243 my $g = init();
244 my $topics = arg();
245 my $tags = arg();
247 if ($topics) {
248 $topics = [ split(':', $topics) ];
251 foreach my $s ($g->stories_by_tag($topics, $tags)) {
252 print join(' ', @{$s}), "\n";
256 'search' => [
257 'search {src} {topic_id} {query}',
258 'Searches stories by content',
259 sub {
260 my $g = init();
261 my $topic_id = arg();
262 my $query = arg();
264 foreach my $s ($g->search_stories($topic_id, $query)) {
265 print $s, "\n";
269 'top_ten' => [
270 'top_ten {src} [{num}]',
271 'Shows the top N stories',
272 sub {
273 my $g = init();
274 my $num = arg_o() || 10;
276 foreach my $s ($g->stories_top_ten($num)) {
277 print join(' ', @{$s}), "\n";
281 'users_by_xdate' => [
282 'users_by_xdate {src} [{max_date}]',
283 'Lists users by expiration date',
284 sub {
285 my $g = init();
286 my $max_date = arg_o() || '99999999999999';
288 foreach my $id ($g->users()) {
289 my $u = $g->user($id);
291 my $xdate = $u->get('xdate');
293 if ($xdate &&
294 $xdate gt Gruta::Data::today() &&
295 $xdate lt $max_date) {
296 print $id, ' ', $u->{email}, ' ', $xdate, "\n";
303 my $cmd = arg();
305 my $c;
307 if (not $c = $X->{$cmd}) {
308 $cmd = undef;
309 usage();
312 # execute
313 $c->[2]();
315 exit 0;
318 sub arg
320 if (@ARGV) {
321 return shift(@ARGV);
324 usage();
328 sub arg_o
330 return shift(@ARGV) || shift;
333 sub init
335 my $src = new_source(arg());
336 my $g = Gruta->new( sources => $src );
338 return $g;
342 sub usage
344 if ($cmd) {
345 my $c = $X->{$cmd};
347 print "Usage: gruta ", $c->[0], "\n\n\t", $c->[1], "\n";
349 else {
350 print "Usage: gruta {cmd} [args...]\n\n";
352 foreach my $k (sort keys %{$X}) {
353 my $c = $X->{$k};
355 print ' ', sprintf('%-50s %s',
356 $c->[0], $c->[1]), "\n";
360 exit 1;
363 sub new_source
365 my $src_str = shift;
366 my $src;
368 if ($src_str =~ /^dbi:/) {
369 $src = Gruta::Source::DBI->new( string => $src_str );
371 elsif ($src_str =~ /^mbox:(.+)/) {
372 my $file = $1;
374 $src = Gruta::Source::Mbox->new(
375 file => $file
378 else {
379 $src = Gruta::Source::FS->new( path => $src_str );
382 return $src;
385 sub get_story
387 my $g = shift;
388 my $topic_id = shift;
389 my $id = shift;
390 my @r = ();
392 my $story = $g->story($topic_id, $id);
394 foreach my $f ($story->fields()) {
395 if ($f ne 'content') {
396 push (@r, $f . ': ' . ($story->get($f) || ''));
400 push(@r, 'tags: ' . join(', ', $story->tags()));
401 push(@r, '');
402 push(@r, $story->get('content'));
403 push(@r, '');
405 return join("\n", @r);
409 sub get_topic
411 my $g = shift;
412 my $topic_id = shift;
413 my @r = ();
415 my $topic = $g->topic($topic_id);
417 foreach my $f ($topic->afields()) {
418 push(@r, $f . ': ' . ($topic->get($f) || ''));
421 return join("\n", @r);
425 sub save_story
427 my $g = shift;
428 my $topic_id = shift;
429 my $id = shift;
430 my $fn = shift;
432 open F, $fn or die "Can't open $fn";
434 my $story = undef;
436 if ($id) {
437 $story = $g->story($topic_id, $id);
439 else {
440 $story = Gruta::Data::Story->new (
441 topic_id => $topic_id,
442 id => $id
446 while (<F>) {
447 chomp();
449 last if /^$/;
451 my ($key, $value) = (/^(\w+):\s*(.*)$/);
453 if ($key eq 'tags') {
454 $story->tags(split(/,\s*/, $value));
456 else {
457 $story->set($key, $value);
461 my $c = join('', <F>);
462 close F;
464 $story->set('content', $c);
466 if ($story->source()) {
467 $story->save();
469 else {
470 $g->insert_story($story);
475 sub save_topic
477 my $g = shift;
478 my $topic_id = shift;
479 my $fn = shift;
481 open F, $fn or die "Can't open $fn";
483 my $topic = undef;
485 if ($topic_id) {
486 $topic = $g->topic($topic_id);
488 else {
489 $topic = Gruta::Data::Topic->new (
490 topic_id => $topic_id,
494 while (<F>) {
495 chomp();
497 last if /^$/;
499 my ($key, $value) = (/^(\w+):\s*(.*)$/);
501 $topic->set($key, $value);
504 if ($topic->source()) {
505 $topic->save();
507 else {
508 $g->insert_topic($topic);