/[BackupPC]/trunk/lib/BackupPC/SearchLib.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/BackupPC/SearchLib.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 117 - (show annotations)
Sun Sep 11 13:05:06 2005 UTC (18 years, 8 months ago) by dpavlin
File size: 14480 byte(s)
added node search

1 #!/usr/bin/perl
2 package BackupPC::SearchLib;
3
4 use strict;
5 use BackupPC::CGI::Lib qw(:all);
6 use BackupPC::Attrib qw(:all);
7 use DBI;
8 use DateTime;
9 use vars qw(%In $MyURL);
10 use Time::HiRes qw/time/;
11
12 my $on_page = 100;
13 my $pager_pages = 10;
14
15 my $dsn = $Conf{SearchDSN};
16 my $db_user = $Conf{SearchUser} || '';
17
18 my $index_path = $Conf{HyperEstraierIndex};
19 if ($index_path) {
20 $index_path = $TopDir . '/' . $index_path;
21 $index_path =~ s#//#/#g;
22 }
23
24 my $dbh;
25
26 sub get_dbh {
27 $dbh ||= DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
28 return $dbh;
29 }
30
31 sub getUnits() {
32 my @ret;
33
34 my $dbh = get_dbh();
35 my $sth = $dbh->prepare(qq{
36 SELECT
37 shares.id as id,
38 hosts.name || ':' || shares.name as share
39 FROM shares
40 JOIN hosts on hostid = hosts.id
41 ORDER BY share
42 } );
43 $sth->execute();
44 push @ret, { 'id' => '', 'share' => '-'}; # dummy any
45
46 while ( my $row = $sth->fetchrow_hashref() ) {
47 push @ret, $row;
48 }
49 return @ret;
50 }
51
52 sub epoch_to_iso {
53 my $t = shift || return;
54 my $iso = BackupPC::Lib::timeStamp(undef, $t);
55 $iso =~ s/\s/ /g;
56 return $iso;
57 }
58
59 sub dates_from_form($) {
60 my $param = shift || return;
61
62 sub mk_epoch_date($$) {
63 my ($name,$suffix) = @_;
64
65 my $yyyy = $param->{ $name . '_year_' . $suffix} || return undef;
66 my $mm .= $param->{ $name . '_month_' . $suffix} ||
67 ( $suffix eq 'from' ? 1 : 12);
68 my $dd .= $param->{ $name . '_day_' . $suffix} ||
69 ( $suffix eq 'from' ? 1 : 31);
70
71 $yyyy =~ s/\D//g;
72 $mm =~ s/\D//g;
73 $dd =~ s/\D//g;
74
75 my $dt = new DateTime(
76 year => $yyyy,
77 month => $mm,
78 day => $dd
79 );
80 print STDERR "mk_epoch_date($name,$suffix) [$yyyy-$mm-$dd] = " . $dt->ymd . " " . $dt->hms . "\n";
81 return $dt->epoch || 'NULL';
82 }
83
84 my @ret = (
85 mk_epoch_date('search_backup', 'from'),
86 mk_epoch_date('search_backup', 'to'),
87 mk_epoch_date('search', 'from'),
88 mk_epoch_date('search', 'to'),
89 );
90
91 return @ret;
92
93 }
94
95
96 sub getWhere($) {
97 my $param = shift || return;
98
99 my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
100
101 my @conditions;
102 push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
103 push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
104 push @conditions, qq{ files.date >= $files_from } if ($files_from);
105 push @conditions, qq{ files.date <= $files_to } if ($files_to);
106
107 print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" | ",@conditions);
108
109 push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
110 push (@conditions, " upper(files.path) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
111
112 return join(" and ", @conditions);
113 }
114
115
116 sub getFiles($) {
117 my ($param) = @_;
118
119 my $offset = $param->{'offset'} || 0;
120 $offset *= $on_page;
121
122 my $dbh = get_dbh();
123
124 my $sql_cols = qq{
125 files.id AS fid,
126 hosts.name AS hname,
127 shares.name AS sname,
128 files.backupnum AS backupnum,
129 files.path AS filepath,
130 files.date AS date,
131 files.type AS type,
132 files.size AS size
133 };
134
135 my $sql_from = qq{
136 FROM files
137 INNER JOIN shares ON files.shareID=shares.ID
138 INNER JOIN hosts ON hosts.ID = shares.hostID
139 INNER JOIN backups ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
140 };
141
142 my $sql_where;
143 my $where = getWhere($param);
144 $sql_where = " WHERE ". $where if ($where);
145
146 my $sql_order = qq{
147 ORDER BY files.date
148 LIMIT $on_page
149 OFFSET ?
150 };
151
152 my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
153 my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
154
155 my $sth = $dbh->prepare($sql_count);
156 $sth->execute();
157 my ($results) = $sth->fetchrow_array();
158
159 $sth = $dbh->prepare($sql_results);
160 $sth->execute( $offset );
161
162 if ($sth->rows != $results) {
163 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
164 $bug =~ s/\s+/ /gs;
165 print STDERR "$bug\n";
166 }
167
168 my @ret;
169
170 while (my $row = $sth->fetchrow_hashref()) {
171 push @ret, $row;
172 }
173
174 $sth->finish();
175 return ($results, \@ret);
176 }
177
178 sub getHyperEstraier_url($) {
179 my ($use_hest) = @_;
180
181 return unless $use_hest;
182
183 use HyperEstraier;
184 my ($index_path, $index_node_url);
185
186 if ($use_hest =~ m#^http://#) {
187 $index_node_url = $use_hest;
188 } else {
189 $index_path = $TopDir . '/' . $index_path;
190 $index_path =~ s#//#/#g;
191 }
192 return ($index_path, $index_node_url);
193 }
194
195 sub getFilesHyperEstraier($) {
196 my ($param) = @_;
197
198 my $offset = $param->{'offset'} || 0;
199 $offset *= $on_page;
200
201 die "no index_path?" unless ($index_path);
202
203 use HyperEstraier;
204
205 my ($index_path, $index_node_url) = getHyperEstraier_url($index_path);
206
207 # open the database
208 my $db;
209 if ($index_path) {
210 $db = HyperEstraier::Database->new();
211 $db->open($index_path, $HyperEstraier::ESTDBREADER);
212 } elsif ($index_node_url) {
213 $db ||= HyperEstraier::Node->new($index_node_url);
214 $db->set_auth('admin', 'admin');
215 } else {
216 die "BUG: unimplemented";
217 }
218
219 # create a search condition object
220 my $cond = HyperEstraier::Condition->new();
221
222 my $q = $param->{'search_filename'};
223 my $shareid = $param->{'search_share'};
224
225 if (length($q) > 0) {
226 # exact match
227 $cond->add_attr("filepath ISTRINC $q");
228
229 $q =~ s/(.)/$1 /g;
230 # set the search phrase to the search condition object
231 $cond->set_phrase($q);
232 }
233
234 my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
235
236 $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
237 $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
238
239 $cond->add_attr("date NUMGE $files_from") if ($files_from);
240 $cond->add_attr("date NUMLE $files_to") if ($files_to);
241
242 $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
243
244 # $cond->set_max( $offset + $on_page );
245 $cond->set_options( $HyperEstraier::Condition::SURE );
246 $cond->set_order( 'date NUMA' );
247
248 # get the result of search
249 my @res;
250 my ($result, $hits);
251
252 if ($index_path) {
253 $result = $db->search($cond, 0);
254 $hits = $result->size;
255 } elsif ($index_node_url) {
256 $result = $db->search($cond, 0);
257 $hits = $result->doc_num;
258 } else {
259 die "BUG: unimplemented";
260 }
261
262 # for each document in result
263 for my $i ($offset .. ($offset + $on_page - 1)) {
264 last if ($i >= $hits);
265
266 my $doc;
267 if ($index_path) {
268 my $id = $result->get($i);
269 $doc = $db->get_doc($id, 0);
270 } elsif ($index_node_url) {
271 $doc = $result->get_doc($i);
272 } else {
273 die "BUG: unimplemented";
274 }
275
276 my $row;
277 foreach my $c (qw/fid hname sname backupnum fiilename filepath date type size/) {
278 $row->{$c} = $doc->attr($c);
279 }
280 push @res, $row;
281 }
282
283 return ($hits, \@res);
284 }
285
286 sub getGzipName($$$)
287 {
288 my ($host, $share, $backupnum) = @_;
289 my $ret = $Conf{GzipSchema};
290
291 $share =~ s/\//_/g;
292 $ret =~ s/\\h/$host/ge;
293 $ret =~ s/\\s/$share/ge;
294 $ret =~ s/\\n/$backupnum/ge;
295
296 return $ret;
297
298 }
299
300 sub getBackupsNotBurned() {
301
302 my $dbh = get_dbh();
303 my $sql = q{
304 SELECT
305 backups.hostID AS hostid,
306 min(hosts.name) AS host,
307 min(shares.name) AS share,
308 backups.num AS backupnum,
309 min(backups.type) AS type,
310 min(backups.date) AS date,
311 min(backups.size) AS size
312 FROM files
313 INNER JOIN shares ON files.shareID=shares.ID
314 INNER JOIN hosts ON hosts.ID = shares.hostID
315 INNER JOIN backups ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
316 GROUP BY
317 backups.hostID, backups.num, backups.shareid
318 ORDER BY min(backups.date)
319 };
320 my $sth = $dbh->prepare( $sql );
321 my @ret;
322 $sth->execute();
323
324 while ( my $row = $sth->fetchrow_hashref() ) {
325 $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
326 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
327 my (undef,undef,undef,undef,undef,undef,undef,$fs_size,undef,undef,undef,undef,undef) =
328 stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
329 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
330 $row->{'fs_size'} = $fs_size;
331 push @ret, $row;
332 }
333
334 return @ret;
335 }
336
337 sub displayBackupsGrid()
338 {
339 my $retHTML = "";
340
341 $retHTML .= <<EOF3;
342 <script language="javascript" type="text/javascript">
343 <!--
344
345 function checkAll(location)
346 {
347 for (var i=0;i<document.forma.elements.length;i++)
348 {
349 var e = document.forma.elements[i];
350 if ((e.checked || !e.checked) && e.name != \'all\') {
351 if (eval("document.forma."+location+".checked")) {
352 e.checked = true;
353 } else {
354 e.checked = false;
355 }
356 }
357 }
358 }
359 //-->
360 </script>
361 EOF3
362 $retHTML .= q{
363 <form name="forma" method="GET" action="$MyURL?action=burn">
364 <input type="hidden" value="burn" name="action">
365 <input type="hidden" value="results" name="search_results">
366 <table style="fview" border="0" cellspacing="0" cellpadding="2">
367 <tr class="tableheader">
368 <td class="tableheader">
369 <input type="checkbox" name="allFiles" onClick="checkAll('allFiles');">
370 </td>
371 <td align="center">Share</td>
372 <td align="center">Backup no</td>
373 <td align="center">Type</td>
374 <td align="center">date</td>
375 <td align="center">age/days</td>
376 <td align="center">size/MB</td>
377 <td align="center">gzip size</td>
378 </tr>
379
380 <tr><td colspan=7 style="tableheader">
381 <input type="submit" value="Burn selected backups on medium" name="submitBurner">
382 </td></tr>
383 };
384
385 my @color = (' bgcolor="#e0e0e0"', '');
386
387 my $i = 0;
388 my $host = '';
389
390 foreach my $backup ( getBackupsNotBurned() ) {
391
392 if ($host ne $backup->{'host'}) {
393 $i++;
394 $host = $backup->{'host'};
395 }
396 my $ftype = "";
397
398 $retHTML .= "<tr" . $color[$i %2 ] . ">";
399 $retHTML .= '<td class="fview"><input type="checkbox" name="fcb' .
400 $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
401 '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
402 '"></td>';
403
404 $retHTML .=
405 '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
406 '<td align="center">' . $backup->{'backupnum'} . '</td>' .
407 '<td align="center">' . $backup->{'type'} . '</td>' .
408 '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
409 '<td align="center">' . $backup->{'age'} . '</td>' .
410 '<td align="right">' . $backup->{'size'} . '</td>' .
411 '<td align="right">' . $backup->{'fs_size'} .'</td>' .
412 "</tr>\n";
413
414
415 }
416
417 $retHTML .= "</table>";
418 $retHTML .= "</form>";
419
420 return $retHTML;
421 }
422
423 sub displayGrid($) {
424 my ($param) = @_;
425
426 my $offset = $param->{'offset'};
427 my $hilite = $param->{'search_filename'};
428
429 my $retHTML = "";
430
431 my $start_t = time();
432
433 my ($results, $files);
434 if ($param->{'use_hest'} && length($hilite) > 0) {
435 ($results, $files) = getFilesHyperEstraier($param);
436 } else {
437 ($results, $files) = getFiles($param);
438 }
439
440 my $dur_t = time() - $start_t;
441 my $dur = sprintf("%0.4fs", $dur_t);
442
443 my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
444
445 if ($results <= 0) {
446 $retHTML .= qq{
447 <p style="color: red;">No results found...</p>
448 };
449 return $retHTML;
450 } else {
451 # DEBUG
452 #use Data::Dumper;
453 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
454 }
455
456
457 $retHTML .= qq{
458 <div>
459 Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
460 </div>
461 <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
462 <tr class="fviewheader">
463 <td></td>
464 <td align="center">Share</td>
465 <td align="center">Type and Name</td>
466 <td align="center">#</td>
467 <td align="center">Size</td>
468 <td align="center">Date</td>
469 <td align="center">Media</td>
470 </tr>
471 };
472
473 my $file;
474
475 sub hilite_html($$) {
476 my ($html, $search) = @_;
477 $html =~ s#($search)#<b>$1</b>#gis;
478 return $html;
479 }
480
481 sub restore_link($$$$$$) {
482 my $type = shift;
483 my $action = 'RestoreFile';
484 $action = 'browse' if (lc($type) eq 'dir');
485 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
486 }
487
488 my $i = $offset * $on_page;
489
490 foreach $file (@{ $files }) {
491 $i++;
492
493 my $typeStr = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
494 $retHTML .= qq{<tr class="fviewborder">};
495
496 $retHTML .= qq{<td class="fviewborder">$i</td>};
497
498 $retHTML .=
499 qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
500 qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
501 qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
502 qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
503 qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
504 qq{<td class="fviewborder">} . '?' . qq{</td>};
505
506 $retHTML .= "</tr>";
507 }
508 $retHTML .= "</table>";
509
510 # all variables which has to be transfered
511 foreach my $n (qw/search_day_from search_month_from search_year_from search_day_to search_month_to search_year_to search_backup_day_from search_backup_month_from search_backup_year_from search_backup_day_to search_backup_month_to search_backup_year_to search_filename offset/) {
512 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
513 }
514
515 my $del = '';
516 my $max_page = int( $results / $on_page );
517 my $page = 0;
518
519 sub page_link($$$) {
520 my ($param,$page,$display) = @_;
521
522 $param->{'offset'} = $page;
523
524 my $html = '<a href = "' . $MyURL;
525 my $del = '?';
526 foreach my $k (keys %{ $param }) {
527 if ($param->{$k}) {
528 $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
529 $del = '&';
530 }
531 }
532 $html .= '">' . $display . '</a>';
533 }
534
535 $retHTML .= '<div style="text-align: center;">';
536
537 if ($offset > 0) {
538 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
539 }
540
541 while ($page <= $max_page) {
542 if ($page == $offset) {
543 $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
544 } else {
545 $retHTML .= $del . page_link($param, $page, $page + 1);
546 }
547
548 if ($page < $offset - $pager_pages && $page != 0) {
549 $retHTML .= " ... ";
550 $page = $offset - $pager_pages;
551 $del = '';
552 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
553 $retHTML .= " ... ";
554 $page = $max_page;
555 $del = '';
556 } else {
557 $del = ' | ';
558 $page++;
559 }
560 }
561
562 if ($offset < $max_page) {
563 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
564 }
565
566 $retHTML .= "</div>";
567
568 return $retHTML;
569 }
570
571 1;

  ViewVC Help
Powered by ViewVC 1.1.26