]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Utils/Fieldmapper.pm
sorting columns for reliable output
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Utils / Fieldmapper.pm
1 package Fieldmapper;
2 use JSON;
3 use Data::Dumper;
4 use base 'OpenSRF::Application';
5
6 use OpenSRF::Utils::Logger;
7 my $log = 'OpenSRF::Utils::Logger';
8
9 use OpenILS::Application::Storage::CDBI;
10 use OpenILS::Application::Storage::CDBI::actor;
11 use OpenILS::Application::Storage::CDBI::action;
12 use OpenILS::Application::Storage::CDBI::asset;
13 use OpenILS::Application::Storage::CDBI::biblio;
14 use OpenILS::Application::Storage::CDBI::config;
15 use OpenILS::Application::Storage::CDBI::metabib;
16 use OpenILS::Application::Storage::CDBI::money;
17
18 use vars qw/$fieldmap $VERSION/;
19
20 _init();
21
22 sub publish_fieldmapper {
23         my ($self,$client,$class) = @_;
24
25         return $fieldmap unless (defined $class);
26         return undef unless (exists($$fieldmap{$class}));
27         return {$class => $$fieldmap{$class}};
28 }
29 __PACKAGE__->register_method(
30         api_name        => 'opensrf.open-ils.system.fieldmapper',
31         api_level       => 1,
32         method          => 'publish_fieldmapper',
33 );
34
35 #
36 # To dump the Javascript version of the fieldmapper struct use the command:
37 #
38 #       PERL5LIB=~/cvs/ILS/OpenSRF/src/perlmods/:~/cvs/ILS/Open-ILS/src/perlmods/ GEN_JS=1 perl -MOpenILS::Utils::Fieldmapper -e 'print "\n";'
39 #
40 # ... adjusted for your CVS sandbox, of course.
41 #
42
43 sub classes {
44         $log->debug("classes: ".join(' -- ', keys %$fieldmap), DEBUG);
45         return () unless (defined $fieldmap);
46         return keys %$fieldmap;
47 }
48
49 sub _init {
50         return if (keys %$fieldmap);
51
52         $fieldmap = 
53         {
54                 'Fieldmapper::action::survey'                   => { hint               => 'asv',
55                                                                      proto_fields       => { questions  => 1,
56                                                                                              responses  => 1 } },
57                 'Fieldmapper::action::survey_question'          => { hint               => 'asvq',
58                                                                      proto_fields       => { answers    => 1,
59                                                                                              responses  => 1 } },
60                 'Fieldmapper::action::survey_answer'            => { hint               => 'asva',
61                                                                      proto_fields       => { responses => 1 } },
62                 'Fieldmapper::action::survey_response'          => { hint               => 'asvr'  },
63                 'Fieldmapper::action::circulation'              => { hint               => 'ac'    },
64                 'Fieldmapper::actor::user'                      => { hint               => 'au',
65                                                                      proto_fields       => { cards      => 1,
66                                                                                              addresses  => 1 } },
67                 'Fieldmapper::actor::user_address'              => { hint => 'aua'    },
68                 'Fieldmapper::actor::org_address'               => { hint => 'aoa'    },
69                 'Fieldmapper::actor::profile'                   => { hint => 'ap'    },
70                 'Fieldmapper::actor::card'                      => { hint => 'ac'    },
71                 'Fieldmapper::config::standing'                 => { hint => 'cst'   },
72                 'Fieldmapper::actor::stat_cat'                  => { hint               => 'asc',
73                                                                      proto_fields       => { entries => 1 } },
74                 'Fieldmapper::actor::stat_cat_entry'            => { hint => 'asce'    },
75                 'Fieldmapper::actor::stat_cat_entry_user_map'   => { hint => 'ascecm'  },
76                 'Fieldmapper::actor::org_unit'                  => { hint               => 'aou',
77                                                                      proto_fields       => { children => 1 } },
78                 'Fieldmapper::actor::org_unit_type'             => { hint               => 'aout',
79                                                                      proto_fields       => { children => 1 } },
80                 
81                 'Fieldmapper::biblio::record_node'              => { hint               => 'brn',
82                                                                      virtual            => 1,
83                                                                      proto_fields       => { children           => 1,
84                                                                                              id                 => 1,
85                                                                                              owner_doc          => 1,
86                                                                                              intra_doc_id       => 1,
87                                                                                              parent_node        => 1,
88                                                                                              node_type          => 1,
89                                                                                              namepsace_uri      => 1,
90                                                                                              name               => 1,
91                                                                                              value              => 1,
92                                                                                            } },
93
94                 'Fieldmapper::metabib::virtual_record'          => { hint               => 'mvr',
95                                                                      virtual            => 1,
96                                                                      proto_fields       => { title              => 1,
97                                                                                              author             => 1,
98                                                                                              doc_id             => 1,
99                                                                                              doc_type           => 1,
100                                                                                              isbn               => 1,
101                                                                                              pubdate            => 1,
102                                                                                              publisher          => 1,
103                                                                                              tcn                => 1,
104                                                                                              subject            => 1,
105                                                                                              types_of_resource  => 1,
106                                                                                              call_numbers       => 1,
107                                                                                              copy_count         => 1,
108                                                                                            } },
109
110                 'Fieldmapper::biblio::record_entry'             => { hint               => 'bre',
111                                                                      proto_fields       => { call_numbers => 1 } },
112                 #'Fieldmapper::biblio::record_marc'             => { hint => 'brx'  }, # now it's inside record_entry
113
114                 'Fieldmapper::money::cash_payment'              => { hint => 'mcp'  },
115                 'Fieldmapper::money::billing'                   => { hint => 'mb'  },
116
117                 'Fieldmapper::config::identification_type'      => { hint => 'cit'  },
118                 'Fieldmapper::config::bib_source'               => { hint => 'cbs'  },
119                 'Fieldmapper::config::metabib_field'            => { hint => 'cmf'  },
120
121                 'Fieldmapper::metabib::metarecord'              => { hint => 'mmr'  },
122                 'Fieldmapper::metabib::title_field_entry'       => { hint => 'mtfe' },
123                 'Fieldmapper::metabib::author_field_entry'      => { hint => 'mafe' },
124                 'Fieldmapper::metabib::subject_field_entry'     => { hint => 'msfe' },
125                 'Fieldmapper::metabib::keyword_field_entry'     => { hint => 'mkfe' },
126                 'Fieldmapper::metabib::full_rec'                => { hint => 'mfr'  },
127                 'Fieldmapper::metabib::record_descriptor'       => { hint => 'mrd'  },
128                 'Fieldmapper::metabib::metarecord_source_map'   => { hint => 'mmrsm'},
129
130                 'Fieldmapper::asset::copy'                      => { hint               => 'acp',
131                                                                      proto_fields       => { stat_cat_entries => 1 } },
132                 'Fieldmapper::asset::stat_cat'                  => { hint               => 'asc',
133                                                                      proto_fields       => { entries => 1 } },
134                 'Fieldmapper::asset::stat_cat_entry'            => { hint => 'asce'    },
135                 'Fieldmapper::asset::stat_cat_entry_copy_map'   => { hint => 'ascecm'  },
136                 'Fieldmapper::asset::copy_note'                 => { hint => 'acpn'    },
137                 'Fieldmapper::asset::call_number'               => { hint               => 'acn',
138                                                                      proto_fields       => { copies => 1 } },
139                 'Fieldmapper::asset::call_number_note'          => { hint => 'acnn'    },
140         };
141
142         #-------------------------------------------------------------------------------
143         # Now comes the evil!  Generate classes
144
145         for my $pkg ( __PACKAGE__->classes ) {
146                 $log->debug("Generating FM class for $pkg", DEBUG);
147                 (my $cdbi = $pkg) =~ s/^Fieldmapper:://o;
148
149                 eval <<"                PERL";
150                         package $pkg;
151                         use base 'Fieldmapper';
152                 PERL
153
154                 $$fieldmap{$pkg}{cdbi} = $cdbi;
155
156                 my $pos = 0;
157                 for my $vfield ( qw/isnew ischanged isdeleted/ ) {
158                         $$fieldmap{$pkg}{fields}{$vfield} = { position => $pos, virtual => 1 };
159                         $pos++;
160                 }
161
162                 if (exists $$fieldmap{$pkg}{proto_fields}) {
163                         for my $pfield ( keys %{ $$fieldmap{$pkg}{proto_fields} } ) {
164                                 $$fieldmap{$pkg}{fields}{$pfield} = { position => $pos, virtual => $$fieldmap{$pkg}{proto_fields}{$pfield} };
165                                 $pos++;
166                         }
167                 }
168
169                 unless ( $$fieldmap{$pkg}{virtual} ) {
170                         for my $col ( sort $cdbi->columns('All') ) {
171                                 $$fieldmap{$pkg}{fields}{$col} = { position => $pos, virtual => 0 };
172                                 $pos++;
173                         }
174                 }
175
176                 JSON->register_class_hint(
177                         hint => $pkg->json_hint,
178                         name => $pkg,
179                         type => 'array',
180                 );
181
182         }
183 }
184
185 sub new {
186         my $self = shift;
187         my $value = shift;
188         $value = [] unless (defined $value);
189         return bless $value => $self->class_name;
190 }
191
192 sub decast {
193         my $self = shift;
194         return [ @$self ];
195 }
196
197 sub DESTROY {}
198
199 sub AUTOLOAD {
200         my $obj = shift;
201         my $value = shift;
202         (my $field = $AUTOLOAD) =~ s/^.*://o;
203         my $class_name = $obj->class_name;
204
205         my $fpos = $field;
206         $fpos  =~ s/^clear_//og ;
207
208         my $pos = $$fieldmap{$class_name}{fields}{$fpos}{position};
209
210         if ($field =~ /^clear_/o) {
211                 {       no strict 'subs';
212                         *{$obj->class_name."::$field"} = sub {
213                                 my $self = shift;
214                                 $self->[$pos] = undef;
215                                 return 1;
216                         };
217                 }
218                 return $obj->$field();
219         }
220
221         die "No field by the name $field in $class_name!"
222                 unless (exists $$fieldmap{$class_name}{fields}{$field});
223
224
225         {       no strict 'subs';
226                 *{$obj->class_name."::$field"} = sub {
227                         my $self = shift;
228                         my $new_val = shift;
229                         $self->[$pos] = $new_val if (defined $new_val);
230                         return $self->[$pos];
231                 };
232         }
233         return $obj->$field($value);
234 }
235
236 sub class_name {
237         my $class_name = shift;
238         return ref($class_name) || $class_name;
239 }
240
241 sub real_fields {
242         my $self = shift;
243         my $class_name = $self->class_name;
244         my $fields = $$fieldmap{$class_name}{fields};
245
246         my @f = grep {
247                         !$$fields{$_}{virtual}
248                 } sort {$$fields{$a}{position} <=> $$fields{$b}{position}} keys %$fields;
249
250         return @f;
251 }
252
253 sub api_level {
254         my $self = shift;
255         return $fieldmap->{$self->class_name}->{api_level};
256 }
257
258
259 sub is_virtual {
260         my $self = shift;
261         return $fieldmap->{$self->class_name}->{virtual};
262 }
263
264 sub json_hint {
265         my $self = shift;
266         return $fieldmap->{$self->class_name}->{hint};
267 }
268
269
270 1;