loads of changes and added things
[spider.git] / perl / Spot.pm
1 #
2 # the dx spot handler
3 #
4 # Copyright (c) - 1998 Dirk Koopman G1TLH
5 #
6 # $Id$
7 #
8
9 package Spot;
10
11 use FileHandle;
12 use DXVars;
13 use DXDebug;
14 use DXUtil;
15 use Julian;
16 use Prefix;
17 use Carp;
18
19 @ISA = qw(Julian);
20
21 use strict;
22 use vars qw($fp $maxspots $defaultspots $maxdays $dirprefix);
23
24 $fp = undef;
25 $maxspots = 50;      # maximum spots to return
26 $defaultspots = 10;    # normal number of spots to return
27 $maxdays = 35;        # normal maximum no of days to go back
28 $dirprefix = "$main::data/spots";
29
30 sub init
31 {
32   mkdir "$dirprefix", 0777 if !-e "$dirprefix";
33 }
34
35 sub prefix
36 {
37   return $dirprefix;
38 }
39
40 # add a spot to the data file (call as Spot::add)
41 sub add
42 {
43   my @spot = @_;    # $freq, $call, $t, $comment, $spotter = @_
44
45   # sure that the numeric things are numeric now (saves time later)
46   $spot[0] = 0 + $spot[0];
47   $spot[2] = 0 + $spot[2];
48   
49   # remove ssid if present on spotter
50   $spot[4] =~ s/-\d+$//o;
51
52   # compare dates to see whether need to open another save file (remember, redefining $fp 
53   # automagically closes the output file (if any))
54   my @date = Julian::unixtoj($spot[2]);
55   $fp = Spot->open(@date, ">>") if (!$fp || Julian::cmp(@date, $fp->{year}, $fp->{day}));
56
57   # save it
58   my $fh = $fp->{fh};
59
60   # add the 'dxcc' country on the end
61   my @dxcc = Prefix::extract($spot[1]);
62   push @spot, (@dxcc > 0 ) ? $dxcc[1]->dxcc() : 0;
63
64   my $buf = join("\^", @spot);
65   $fh->print($buf, "\n");
66   
67   return $buf;
68 }
69
70 # search the spot database for records based on the field no and an expression
71 # this returns a set of references to the spots
72 #
73 # the expression is a legal perl 'if' statement with the possible fields indicated
74 # by $f<n> where :-
75 #
76 #   $f0 = frequency
77 #   $f1 = call
78 #   $f2 = date in unix format
79 #   $f3 = comment
80 #   $f4 = spotter
81 #   $f5 = dxcc country
82 #
83 # In addition you can specify a range of days, this means that it will start searching
84 # from <n> days less than today to <m> days less than today
85 #
86 # Also you can select a range of entries so normally you would get the 0th (latest) entry
87 # back to the 5th latest, you can specify a range from the <x>th to the <y>the oldest.
88 #
89 # This routine is designed to be called as Spot::search(..)
90 #
91
92 sub search
93 {
94   my ($expr, $dayfrom, $dayto, $from, $to) = @_;
95   my $eval;
96   my @out;
97   my $ref;
98   my $i;
99   my $count;
100   my @today = Julian::unixtoj(time);
101   my @fromdate;
102   my @todate;
103   
104   if ($dayfrom > 0) {
105     @fromdate = Julian::sub(@today, $dayfrom);
106   } else {
107     @fromdate = @today;
108         $dayfrom = 0;
109   }
110   if ($dayto > 0) {
111     @todate = Julian::sub(@fromdate, $dayto);
112   } else {
113     @todate = Julian::sub(@fromdate, $maxdays);
114   }
115   if ($from || $to) {
116     $to = $from + $maxspots if $to - $from > $maxspots || $to - $from <= 0;
117   } else {
118     $from = 0;
119         $to = $defaultspots;
120   }
121
122   $expr =~ s/\$f(\d)/\$ref->[$1]/g;               # swap the letter n for the correct field name
123 #  $expr =~ s/\$f(\d)/\$spots[$1]/g;               # swap the letter n for the correct field name
124   
125   dbg("search", "expr='$expr', spotno=$from-$to, day=$dayfrom-$dayto\n");
126   
127   # build up eval to execute
128   $eval = qq(
129 #    while (<\$fh>) {
130 #         chomp;
131 #         my \@spots = split /\\^/o;
132 #         if ($expr) {                # note NO \$expr
133 #           \$count++;
134 #               next if \$count < \$from;                  # wait until from 
135 #               push(\@out, \\\@spots);
136 #               last LOOP if \$count >= \$to;                  # stop after to
137 #         }
138 #       }
139     my \$c;
140         my \$ref;
141     for (\$c = \$#spots; \$c >= 0; \$c--) {
142           \$ref = \$spots[\$c];
143           if ($expr) {
144             \$count++;
145                 next if \$count < \$from;                  # wait until from 
146         push(\@out, \$ref);
147                 last LOOP if \$count >= \$to;                  # stop after to
148           }
149     }
150   );
151
152 LOOP:
153   for ($i = 0; $i < $maxdays; ++$i) {             # look thru $maxdays worth of files only
154     my @now = Julian::sub(@fromdate, $i);         # but you can pick which $maxdays worth
155         last if Julian::cmp(@now, @todate) <= 0;         
156         
157         my @spots = ();
158         my $fp = Spot->open(@now);  # get the next file
159         if ($fp) {
160           my $fh = $fp->{fh};
161           my $in;
162           foreach $in (<$fh>) {
163             chomp $in;
164        push @spots, [ split('\^', $in) ];
165           }
166           eval $eval;               # do the search on this file
167           return ("Spot search error", $@) if $@;
168         }
169   }
170
171   return @out;
172 }
173
174 # open a spot file of the Julian day
175 sub open
176 {
177   my $pkg = shift;
178   return Julian::open("spot", $dirprefix, @_);
179 }
180
181 # close a spot file
182 sub close
183 {
184   # do nothing, unreferencing or overwriting the $self will close it  
185 }
186
187 # format a spot for user output in 'broadcast' mode
188 sub formatb
189 {
190   my @dx = @_;
191   my $t = ztime($dx[2]);
192   return sprintf "DX de %-7.7s: %13.1f %-12.12s %-30s<%s>", $dx[4], $dx[0], $dx[1], $dx[3], $t ;
193 }
194
195 # format a spot for user output in list mode
196 sub formatl
197 {
198   my @dx = @_;
199   my $t = ztime($dx[2]);
200   my $d = cldate($dx[2]);
201   return sprintf "%9.1f %-12s %s %s %-30s<%s>", $dx[0], $dx[1], $d, $t, $dx[3], $dx[4] ;
202 }
203
204 1;