File Coverage

blib/lib/DataCube/Schema.pm
Criterion Covered Total %
statement 28 180 15.5
branch 3 42 7.1
condition 2 32 6.2
subroutine 8 36 22.2
pod 8 33 24.2
total 49 323 15.1


line stmt bran cond sub pod time code
1              
2              
3              
4             package DataCube::Schema;
5              
6 1     1   1143 use strict;
  1         3  
  1         35  
7 1     1   6 use warnings;
  1         2  
  1         28  
8 1     1   7 use Digest::MD5;
  1         1  
  1         2380  
9              
10             sub new {
11 1     1 0 1107 my($class,%opts) = @_;
12 1   33     18 bless {
13             %opts,
14             parents => {},
15             measures => [],
16             hierarchies => [],
17             computed_measures => [],
18             }, ref($class) || $class;
19             }
20              
21             sub add_hierarchy {
22 4     4 1 12 my($self,@hierarchy) = @_;
23 4         8 $self->add_strict_hierarchy(undef,@hierarchy);
24             }
25              
26             sub add_strict_hierarchy {
27 4     4 1 8 my($self,@hierarchy) = @_;
28 4         10 for(my $i = 0; $i < $#hierarchy; $i++){
29 7 100       18 next unless my $parent = $hierarchy[$i];
30 3         5 my $child = $hierarchy[$i+1];
31 3         14 $self->{parents}->{$parent}->{$child} = undef;
32             }
33 4         7 $self->{field_names}->{$_} = undef for grep { defined } @hierarchy;
  11         34  
34 4         6 push @{$self->{hierarchies}}, [@hierarchy];
  4         18  
35             }
36              
37             sub initialize {
38 0     0 0 0 my($self,%opts) = @_;
39 0         0 $self->{fields} = [sort keys %{$self->{field_names}}];
  0         0  
40 0         0 my $name = join("\t", @{$self->{fields}});
  0         0  
41 0 0       0 $self->{name} = length($name) ? $name : 'overall';
42 0         0 $self->{name_digest} = Digest::MD5->new->add($self->{name})->hexdigest;
43 0 0 0     0 field_count:{
44 0         0 ($self->{field_count}) = 0 and last field_count if $self->{name} eq 'overall';
45 0         0 $self->{field_count} = $self->{name} =~ s/\t/\t/g + 1;
46             }
47             measure_string:
48 0         0 for(my $i = 0; $i < @{$self->{measures}}; $i++){
  0         0  
49 0         0 my @measure = @{$self->{measures}->[$i]};
  0         0  
50 0 0 0     0 next measure_string if $self->{measures}->[$i][2] || $self->{measures}->[$i][0] eq 'key_count';
51 0         0 $self->{measures}->[$i][2] = join('__',@{$self->{measures}->[$i]});
  0         0  
52             }
53 0         0 $self->{initialized} = 1;
54 0         0 return $self;
55             }
56              
57             sub columns {
58 0     0 0 0 my($self) = @_;
59 0         0 return $self->fields, $self->measure_names
60             }
61              
62             sub measure_names {
63 0     0 0 0 my($self) = @_;
64 0         0 map { $_->[2] } $self->measures
  0         0  
65             }
66              
67             sub confine_to {
68 0     0 1 0 my($self,@fields) = @_;
69 0         0 $self->{confine_to} = join("\t",sort @fields);
70 0         0 return $self;
71             }
72              
73             sub confine_to_base {
74 0     0 0 0 my($self) = @_;
75 0         0 $self->{confine_to_base} = 1;
76 0         0 return $self;
77             }
78              
79             sub is_confined {
80 0     0 0 0 my($self) = @_;
81 0         0 return $self->{confine_to};
82             }
83              
84             sub strict_dimensions {
85 0     0 0 0 my($self) = @_;
86 0 0       0 return [] unless $self->has_strict_hierarchies;
87 0         0 my $hierarchies = $self->hierarchies;
88 0         0 my @dims;
89 0         0 for(@$hierarchies){
90 0 0       0 push @dims, $_->[0] if defined $_->[0];
91             }
92 0         0 return \@dims;
93             }
94              
95             sub has_strict_hierarchies {
96 0     0 0 0 my($self) = @_;
97 0         0 my $hierarchies = $self->hierarchies;
98 0         0 for(@$hierarchies){
99 0 0       0 return 1 if defined $_->[0];
100             }
101 0         0 return 0;
102             }
103              
104             sub has_asserted_lattice_points {
105 0     0 0 0 my($self) = @_;
106 0         0 my $points = $self->{asserted_lattice_points};
107 0 0 0     0 return 1 if $points
      0        
      0        
108             && ref($points)
109             && ref($points) =~ /^hash$/i
110             && keys %$points;
111 0         0 return 0;
112             }
113              
114             sub add_strict_dimension {
115 0     0 1 0 my($self,$dimension) = @_;
116 0         0 $self->add_strict_hierarchy($dimension);
117             }
118              
119             sub add_dimension {
120 3     3 1 13 my($self,$dimension) = @_;
121 3         6 $self->add_hierarchy($dimension);
122             }
123              
124             sub add_measure {
125 3     3 1 13 my($self,@measure) = @_;
126 3 50 33     9 @measure = ('key_count') if @measure == 1 && $measure[0] eq 'count';
127 3         4 push @{$self->{measures}}, [@measure];
  3         9  
128             }
129              
130             sub add_computed_measure {
131 0     0 0   my($self,@measure) = @_;
132 0           push @{$self->{computed_measures}}, [@measure];
  0            
133             }
134              
135             sub suppress_lattice_point {
136 0     0 1   my($self,@point) = @_;
137 0           my $point = join("\t",sort @point);
138 0           $self->{suppressed_lattice_points}->{$point} = undef;
139             }
140              
141             sub filter_lattice_point {
142 0     0 0   my( $self, $code ) = @_;
143 0           push @{ $self->{lattice_point_filters} }, $code;
  0            
144             }
145              
146             sub assert_lattice_point {
147 0     0 1   my($self,@point,%point,$name) = @_;
148             assert_opts:{
149 0 0         if(@point % 2 == 0){
  0            
150 0           %point = @point;
151 0   0       $point{dims} ||= $point{dimensions};
152 0 0 0       if($point{dims} && ref($point{dims}) =~ /^array$/i){
153 0           @point = @{$point{dims}};
  0            
154 0 0         $name = $point{name} if $point{name};
155             }
156             }
157             }
158 0           my $point = join("\t",sort @point);
159 0           $self->{lattice_point_names}->{$point} = $name;
160 0           $self->{asserted_lattice_points}->{$point} = undef;
161 0           return $self;
162             }
163              
164             sub is_initialized {
165 0     0 0   my($self) = @_;
166 0           return $self->{initialized};
167             }
168              
169             sub measures {
170 0     0 0   my($self) = @_;
171 0           @{$self->{measures}}
  0            
172             }
173              
174             sub hierarchies {
175 0     0 0   my($self) = @_;
176 0           return $self->{hierarchies}
177             }
178              
179             sub field_names {
180 0     0 0   my($self) = @_;
181 0           sort keys %{$self->{field_names}}
  0            
182             }
183              
184             sub fields {
185 0     0 0   my($self) = @_;
186 0           return @{$self->{fields}};
  0            
187             }
188              
189             sub field_count {
190 0     0 0   my($self) = @_;
191 0           return $self->{field_count};
192             }
193              
194             sub measure_count {
195 0     0 0   my($self) = @_;
196 0           return scalar @{$self->{measures}};
  0            
197             }
198              
199             sub name {
200 0     0 0   my($self) = @_;
201 0           return $self->{name};
202             }
203              
204             sub name_digest {
205 0     0 0   my($self) = @_;
206 0           return $self->{name_digest};
207             }
208              
209             sub lattice_point_name {
210 0     0 0   my($self) = @_;
211 0           return $self->{lattice_point_name};
212             }
213              
214             sub asserted_lattice_points {
215 0     0 0   my($self) = @_;
216 0           return $self->{asserted_lattice_points};
217             }
218              
219             sub safe_file_name {
220 0     0 0   my($self) = @_;
221 0           my $name = $self->name;
222 0           (my $file_name = $name) =~ s/\t+/__/g;
223 0 0         $file_name = $self->lattice_point_name
224             if defined($self->lattice_point_name);
225 0           return $file_name;
226             }
227              
228             sub pg_types {
229 0     0 0   my($self) = @_;
230            
231 0           my %types;
232              
233 0           for( $self->fields ) {
234 0           $types{ $_ } = 'text'
235             }
236              
237 0           for( $self->measures ) {
238 0 0         if( $_->[0] eq 'sum' ) {
239 0           $types{ $_->[2] } = 'numeric not null';
240             next
241 0           }
242 0           die "Measure $_->[0] not yet implemented in pg_types";
243             }
244            
245 0           %types;
246             }
247              
248              
249              
250              
251             sub check_conflicts {
252 0     0 0   my($self) = @_;
253 0           my @conflicts;
254            
255 0 0 0       strict_assertions:{
256 0           last strict_assertions unless $self->has_asserted_lattice_points && $self->has_strict_hierarchies;
257 0           my $strict = $self->strict_dimensions;
258 0           my $points = $self->asserted_lattice_points;
259 0           for(@$strict){
260 0           my $dim = $_;
261 0           for(keys %$points){
262 0           my @fields = split/\t/,$_,-1;
263 0           my %fields = map { $_ => undef } @fields;
  0            
264 0 0         push @conflicts, {
265             category => 'strict dimensions versus asserted lattice points',
266             dimension => $dim,
267             lattice_point => $_,
268             message => "\n\n\tThe dimension\n\n\t\t$dim\n\n\tis marked as strict ".
269             "but this conflicts with the asserted lattice point:\n\n\t\t$_\n\n\t" .
270             "which does not contain this dimension\n",
271             } unless exists $fields{$dim};
272             }
273             }
274             }
275            
276             confine:{
277 0 0         if(my $confine = $self->is_confined){
  0            
278 0           my @confine = split/\t/,$confine,-1;
279 0           my %confine = map { $_ => undef } @confine;
  0            
280 0           for(@confine){
281 0           my $dim = $_;
282 0 0         unless(exists $self->{field_names}->{$_}){
283 0           push @conflicts, {
284             category => 'confined lattice point is not valid',
285             confine => $confine,
286             dimension => $_,
287             message => "\n\n\tThe dimension\n\n\t\t$dim\n\n\tis confined ".
288             "but no such dimension exists in your schema\n",
289             };
290             }
291 0           for(keys %{$self->{parents}}){
  0            
292 0           my $parent = $_;
293 0 0 0       if(exists $self->{parents}->{$parent}->{$dim} && ! exists $confine{$parent}){
294 0           push @conflicts, {
295             category => 'confined lattice point breaks hierarchical constraints',
296             confine => $confine,
297             dimension => $dim,
298             parent => $parent,
299             message => "\n\n\tThe dimension\n\n\t\t$dim\n\n\tis confined ".
300             "but the parent:\n\n\t\t$parent\n\n\tis not included\n",
301             };
302             }
303             }
304             }
305             }
306             }
307            
308 0 0         if(@conflicts){
309 0           printf "\nconflicts were detected in your schema that must be resolved before cube creation:\n";
310 0           print '-'x100,"\n\n";
311 0           for(@conflicts){
312 0           my $reason = $_;
313 0           for(sort keys %$reason){
314 0 0         next if /^message$/i;
315 0           printf " %-15s => %s\n",$_,$reason->{$_};
316             }
317 0           print "\n message:" . $reason->{message};
318 0           print "\n",'-'x100,"\n";
319             }
320 0           die "\nplease fix these conflicts before continuing\n\n";
321             }
322            
323 0           return $self;
324             }
325              
326              
327              
328              
329             1;
330              
331              
332              
333              
334              
335              
336             __END__