Subversion Repositories DevTools

Rev

Rev 1295 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1293 dpurdie 1
###############################################################################
2
# Codestriker: Copyright (c) 2001, 2002 David Sitsky.  All rights reserved.
3
# sits@users.sourceforge.net
4
#
5
# This program is free software; you can redistribute it and modify it under
6
# the terms of the GPL.
7
 
8
# Model object for handling comment data.
9
 
10
package Codestriker::Model::Comment;
11
 
12
use strict;
13
use Encode qw(decode_utf8);
14
 
15
use Codestriker::DB::DBI;
16
 
17
sub new {
18
    my $class = shift;
19
    my $self = {};
20
 
21
    $self->{id} = 0;
22
    $self->{topicid} = 0;
23
    $self->{fileline} = 0;
24
    $self->{filenumber} = 0;
25
    $self->{filenew} = 0;
26
    $self->{author} = '';
27
    $self->{data} = '';
28
    $self->{date} = Codestriker->get_timestamp(time);
29
    $self->{version} = 0;
30
    $self->{db_creation_ts} = "";
31
    $self->{db_modified_ts} = "";
32
    $self->{creation_ts} = "";
33
    $self->{modified_ts} = "";
34
    $self->{metrics} = undef;
35
 
36
    bless $self, $class;
37
    return $self;
38
}
39
 
40
# Create a new comment with all of the specified properties.  Ensure that the
41
# associated commentstate record is created/updated.
42
sub create {
43
    my ($self, $topicid, $fileline, $filenumber, $filenew, $author, $data,
44
	$metrics) = @_;
45
 
46
    my $timestamp = Codestriker->get_timestamp(time);
47
 
48
    # Obtain a database connection.
49
    my $dbh = Codestriker::DB::DBI->get_connection();
50
 
51
    # Check if a comment has been made against this line before.
52
    my $select_commentstate =
53
	$dbh->prepare_cached('SELECT version, id ' .
54
			     'FROM commentstate ' .
55
			     'WHERE topicid = ? AND fileline = ? AND '.
56
			     'filenumber = ? AND filenew = ?');
57
    my $success = defined $select_commentstate;
58
    $success &&= $select_commentstate->execute($topicid, $fileline,
59
					       $filenumber, $filenew);
60
    my $commentstateid = 0;
61
    my $version = 0;
62
    my $creation_ts = "";
63
    if ($success) {
64
	($version, $commentstateid) =
65
	    $select_commentstate->fetchrow_array();
66
	$success &&= $select_commentstate->finish();
67
	if (! defined $version) {
68
	    # A comment has not been made on this particular line yet,
69
	    # create the commentstate row now.  Note the old column of
70
	    # state has its value set to -100 so the data migration code
71
	    # in checksetup.pl knows this is a new row that can be
72
	    # ignored.
73
	    $creation_ts = $timestamp;
74
	    my $insert = $dbh->prepare_cached('INSERT INTO commentstate ' .
75
					      '(topicid, fileline, ' .
76
					      'filenumber, filenew, ' .
77
					      'state, version, creation_ts, ' .
78
					      'modified_ts) VALUES ' .
79
					      '(?, ?, ?, ?, ?, ?, ?, ?)');
80
	    $success &&= defined $insert;
81
	    $success &&= $insert->execute($topicid, $fileline, $filenumber,
82
					  $filenew, -100, 0,
83
					  $creation_ts, $creation_ts);
84
	    $success &&= $insert->finish();
85
	} else {
86
	    # Update the commentstate record.
87
	    my $update = $dbh->prepare_cached('UPDATE commentstate SET ' .
88
					      'version = ?, ' .
89
					      'modified_ts = ? ' .
90
					      'WHERE topicid = ? AND ' .
91
					      'fileline = ? AND ' .
92
					      'filenumber = ? AND ' .
93
					      'filenew = ?');
94
	    $success &&= defined $update;
95
	    $success &&= $update->execute(++$version,
96
					  $timestamp,
97
					  $topicid, $fileline, $filenumber,
98
					  $filenew);
99
	    $success &&= $update->finish();
100
	}
101
 
102
	# Determine the commentstateid that may have been just created.
103
	$success &&= $select_commentstate->execute($topicid, $fileline,
104
						   $filenumber, $filenew);
105
	if ($success) {
106
	    ($version, $commentstateid) = 
107
		$select_commentstate->fetchrow_array();
108
	}
109
	$success &&= $select_commentstate->finish();
110
 
111
	# Create the comment record.
112
	my $insert_comment =
113
	    $dbh->prepare_cached('INSERT INTO commentdata ' .
114
				 '(commentstateid, '.
115
				 'commentfield, author, creation_ts) ' .
116
				 'VALUES (?, ?, ?, ?)');
117
	my $success = defined $insert_comment;
118
 
119
	# Create the comment row.
120
	$success &&= $insert_comment->execute($commentstateid, $data,
121
					      $author, $timestamp);
122
	$success &&= $insert_comment->finish();
123
 
124
	# Now handle any commentmetric rows.
125
	update_comment_metrics($commentstateid, $metrics, $dbh);
126
    }
127
 
128
    $self->{id} = $commentstateid;
129
    $self->{topicid} =  $topicid;
130
    $self->{fileline} = $fileline;
131
    $self->{filenumber} = $filenumber;
132
    $self->{filenew} = $filenew;
133
    $self->{author} = $author;
134
    $self->{data} = $data;
135
    $self->{date} = $timestamp;
136
    $self->{version} = $version;
137
    $self->{db_creation_ts} = $creation_ts;
138
    $self->{creation_ts} = Codestriker->format_timestamp($creation_ts);
139
    $self->{db_modified_ts} = $timestamp;
140
    $self->{modified_ts} = Codestriker->format_timestamp($timestamp);
141
 
142
    # Update the metrics into the object as a hash.
143
    foreach my $metric (@{ $metrics }) {
144
	$self->{metrics}->{$metric->{name}} = $metric->{value};
145
    }
146
 
147
    # Get the filename, for the new comment.
148
    my $get_filename = $dbh->prepare_cached('SELECT filename ' .
149
					    'FROM topicfile ' .
150
					    'WHERE topicid = ? AND ' .
151
					    'sequence = ?');
152
    $success &&= defined $get_filename;
153
    $success &&= $get_filename->execute($topicid, $filenumber);
154
 
155
    ( $self->{filename} ) = $get_filename->fetchrow_array();
156
 
157
    $select_commentstate->finish();
158
    $get_filename->finish();
159
 
160
    Codestriker::DB::DBI->release_connection($dbh, $success);
161
    die $dbh->errstr if !$success;
162
}
163
 
164
 
165
# Update the comment metrics for a specific commentstate.  Note the rows for
166
# a specific metric may or may not already exist.
167
sub update_comment_metrics {
168
    my ($commentstateid, $metrics, $dbh) = @_;
169
 
170
    # Now create any necessary commentmetric rows.  Note its possible this
171
    # may refer to existing data which needs to be updated, or could be
172
    # new metric data.
173
    eval {
174
	if (defined $metrics) {
175
	    foreach my $metric (@{ $metrics }) {
176
		# Check if a value for this metric name has been created
177
		# already.
178
		my $select_metric =
179
		    $dbh->prepare_cached('SELECT COUNT(id) ' .
180
					 'FROM commentstatemetric ' .
181
					 'WHERE id = ? AND name = ?');
182
		$select_metric->execute($commentstateid, $metric->{name});
183
		my $count;
184
		($count) = $select_metric->fetchrow_array();
185
		$select_metric->finish();
186
		if ($count == 0) {
187
		    # Need to create a new row for this metric.
188
		    my $insert_metric =
189
			$dbh->prepare_cached('INSERT INTO commentstatemetric '.
190
					     '(id, name, value) VALUES ' .
191
					     '(?, ?, ?)');
192
		    $insert_metric->execute($commentstateid, $metric->{name},
193
					    $metric->{value});
194
		    $insert_metric->finish();
195
		} else {
196
		    # Need to update this row for this metric.
197
		    my $update_metric =
198
			$dbh->prepare_cached('UPDATE commentstatemetric ' .
199
					     'SET value = ? ' .
200
					     'WHERE id = ? AND name = ?');
201
		    $update_metric->execute($metric->{value}, $commentstateid,
202
					    $metric->{name});
203
		    $update_metric->finish();
204
		}
205
	    }
206
	}
207
    };
208
    if ($@) {
209
	warn "Unable to update comment state metric data because $@\n";
210
	eval { $dbh->rollback() };
211
    }
212
}
213
 
214
# This function returns as a list the authors emails address that have entered 
215
# comments against a topic.
216
sub read_authors
217
{
218
   my ($type, $topicid ) = @_;
219
 
220
    # Obtain a database connection.
221
    my $dbh = Codestriker::DB::DBI->get_connection();
222
 
223
    # Store the results into an array of objects.
224
    my @results;
225
 
226
    # Retrieve all of the comment information for the specified topicid.
227
    my $select_comment =
228
	$dbh->prepare_cached('SELECT distinct(commentdata.author) ' .
229
			     'FROM commentdata, commentstate ' .
230
			     'WHERE commentstate.topicid = ? AND ' .
231
			     'commentstate.id = commentdata.commentstateid ');
232
 
233
    my $success = defined $select_comment;
234
    my $rc = $Codestriker::OK;
235
    $success &&= $select_comment->execute($topicid);
236
 
237
    # Store the results into the referenced arrays.
238
    if ($success) {
239
	my @data;
240
	while (@data = $select_comment->fetchrow_array()) {
241
	    push @results, $data[0];
242
	}
243
	$select_comment->finish();
244
    }
245
 
246
    Codestriker::DB::DBI->release_connection($dbh, $success);
247
    die $dbh->errstr unless $success;
248
 
249
    return @results;   
250
}
251
 
252
# Return all of the comments made for a specified topic. This should only be
253
# called be called by the Topic object.
254
sub read_all_comments_for_topic($$) {
255
    my ($type, $topicid) = @_;
256
 
257
    # Obtain a database connection.
258
    my $dbh = Codestriker::DB::DBI->get_connection();
259
 
260
    # Determine if we are using Oracle, since it can't handle LEFT OUTER JOINs.
261
    my $using_oracle = $Codestriker::db =~ /^DBI:Oracle/i;
262
 
263
    # Store the results into an array of objects.
264
    my @results = ();
265
 
266
    # Retrieve all of the comment information for the specified topicid.
267
    my $select_comment =
268
	$dbh->prepare_cached('SELECT commentdata.commentfield, ' .
269
			     'commentdata.author, ' .
270
			     'commentstate.fileline, ' .
271
			     'commentstate.filenumber, ' .
272
			     'commentstate.filenew, ' .
273
			     'commentdata.creation_ts, ' .
274
			     'topicfile.filename, ' .
275
			     'commentstate.version, ' .
276
			     'commentstate.id, ' .
277
			     'commentstate.creation_ts, ' .
278
			     'commentstate.modified_ts ' .
279
			     'FROM commentdata, commentstate ' .
280
			     ($using_oracle ?
281
			      (', topicfile WHERE commentstate.topicid = ? ' .
282
			       'AND commentstate.id = commentdata.commentstateid ' .
283
			       'AND topicfile.topicid = commentstate.topicid(+) ' .
284
			       'AND topicfile.sequence = commentstate.filenumber(+) ') :
285
			      ('LEFT OUTER JOIN topicfile ON ' .
286
			       'commentstate.topicid = topicfile.topicid AND ' .
287
			       'commentstate.filenumber = topicfile.sequence ' .
288
			       'WHERE commentstate.topicid = ? ' .
289
			       'AND commentstate.id = commentdata.commentstateid ')) .
290
			     'ORDER BY ' .
291
			     'commentstate.filenumber, ' .
292
			     'commentstate.fileline, ' .
293
			     'commentstate.filenew, ' .
294
			     'commentdata.creation_ts');
295
    my $success = defined $select_comment;
296
    my $rc = $Codestriker::OK;
297
    $success &&= $select_comment->execute($topicid);
298
 
299
    # Store the results into the referenced arrays.
300
    if ($success) {
301
	my @data;
302
	while (@data = $select_comment->fetchrow_array()) {
303
	    my $comment = Codestriker::Model::Comment->new();
304
	    $comment->{topicid} =  $topicid;            
305
	    $comment->{data} = decode_utf8($data[0]);
306
	    $comment->{author} = $data[1];
307
	    $comment->{fileline} = $data[2];
308
	    $comment->{filenumber} = $data[3];
309
	    $comment->{filenew} = $data[4];
310
	    $comment->{date} = Codestriker->format_timestamp($data[5]);
311
	    $comment->{filename} = decode_utf8($data[6]);
312
	    $comment->{version} = $data[7];
313
	    $comment->{id} = $data[8];
314
	    $comment->{db_creation_ts} = $data[9];
315
	    $comment->{creation_ts} = Codestriker->format_timestamp($data[9]);
316
	    $comment->{db_modified_ts} = $data[10];
317
	    $comment->{modified_ts} = Codestriker->format_timestamp($data[10]);
318
	    push @results, $comment;
319
	}
320
	$select_comment->finish();
321
    }
322
 
323
    # Now for each comment returned, retrieve the comment metrics data as well.
324
    foreach my $comment (@results) {
325
	my $select_metric =
326
	    $dbh->prepare_cached('SELECT name, value ' .
327
				 'FROM commentstatemetric ' .
328
				 'WHERE id = ?');
329
	$select_metric->execute($comment->{id});
330
	my %metrics = ();
331
	my @data;
332
	while (@data = $select_metric->fetchrow_array()) {
333
	    $metrics{$data[0]} = $data[1];
334
	}
335
	$select_metric->finish();
336
 
337
	# Update this comment update with the list of metrics associated with
338
	# it.
339
	$comment->{metrics} = \%metrics;
340
    }
341
 
342
    Codestriker::DB::DBI->release_connection($dbh, $success);
343
 
344
    return @results;
345
}
346
 
347
# Return all of the comments made for a specified topic filtered by
348
# author and metric values.  If a filter parameter is not defined, then
349
# it is ignored.
350
sub read_filtered {
351
    my ($type, $topicid, $filtered_by_author, $metric_filter) = @_;
352
 
353
    my %metric_filter = %{ $metric_filter };
354
 
355
    # Read all of the comments from the database. 
356
    my @comments = $type->read_all_comments_for_topic($topicid);
357
 
358
    # Now filter out comments that don't match the author and metric 
359
    # filter.
360
    @comments = grep { 
361
        my $comment = $_;
362
        my $keep_comment = 1;
363
 
364
        # Check for filters via the comment author name, handle email
365
        # SPAM filtering.
366
        my $filteredAuthor =
367
            	    Codestriker->filter_email($comment->{author});
368
        my $filteredByAuthor =
369
            	    Codestriker->filter_email($filtered_by_author);
370
 
371
        if ($filteredByAuthor ne "" && $filteredAuthor ne $filteredByAuthor) {
372
	    # Don't keep this record.
373
	    $keep_comment = 0;
374
	}
375
	else {
376
	    # Check if the metric values match for each key.
377
	    foreach my $metric (keys %metric_filter) {
378
		if ($comment->{metrics}->{$metric} ne
379
		    $metric_filter{$metric}) {
380
		    $keep_comment = 0;
381
		    last;
382
		}
383
	    }
384
	}
385
 
386
	# Indicate whether this comment should be kept or not.
387
 	$keep_comment;
388
    } @comments;
389
 
390
    return @comments;
391
}
392
 
393
# Update the specified metric for the specified commentstate.  The version
394
# parameter indicates what version of the commentstate the user was operating
395
# on.
396
sub change_state {
397
    my ($self, $metric_name, $metric_value, $version) = @_;
398
 
399
    # Obtain a database connection.
400
    my $dbh = Codestriker::DB::DBI->get_connection();
401
 
402
    my $timestamp = Codestriker->get_timestamp(time);   
403
 
404
    # Check that the version reflects the current version in the DB.
405
    my $select_comments =
406
	$dbh->prepare_cached('SELECT id, version ' .
407
			     'FROM commentstate ' .
408
			     'WHERE topicid = ? AND fileline = ? AND ' .
409
			     'filenumber = ? AND filenew = ?');
410
    my $update_comments =
411
	$dbh->prepare_cached('UPDATE commentstate SET version = ?, ' .
412
			     'modified_ts = ? ' .
413
			     'WHERE id = ?');
414
 
415
    my $success = defined $select_comments && defined $update_comments;
416
    my $rc = $Codestriker::OK;
417
 
418
    # Retrieve the current comment data.
419
    $success &&= $select_comments->execute($self->{topicid}, 
420
     				           $self->{fileline},
421
                                           $self->{filenumber},
422
					   $self->{filenew});
423
 
424
    # Make sure that the topic still exists, and is therefore valid.
425
    my ($id, $current_version);
426
    if ($success && 
427
	! (($id, $current_version)
428
	   = $select_comments->fetchrow_array())) {
429
	# Invalid topic id.
430
	$success = 0;
431
	$rc = $Codestriker::INVALID_TOPIC;
432
    }
433
    $success &&= $select_comments->finish();
434
 
435
    # Check the version number.
436
    if ($success && $version != $self->{version}) {
437
	$success = 0;
438
	$rc = $Codestriker::STALE_VERSION;
439
    }
440
 
441
    # Now update the version number for commentstate.
442
    $self->{version} = $self->{version} + 1;
443
    $self->{metrics}->{$metric_name} = $metric_value;
444
    $self->{modified_ts} = Codestriker->format_timestamp($timestamp);
445
    $success &&= $update_comments->execute($self->{version},
446
					   $timestamp,
447
					   $id);
448
 
449
    # Now update the commentstatemetric row for this metric.
450
    my $metrics = [ { name => $metric_name, value => $metric_value } ];
451
    update_comment_metrics($id, $metrics, $dbh);
452
 
453
    Codestriker::DB::DBI->release_connection($dbh, $success);
454
 
455
    return $rc;
456
}
457
 
458
1;