]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_builder.js
tranform and ui updates
[Evergreen.git] / Open-ILS / web / reports / oils_rpt_builder.js
1 /** initializes reports, some basid display settings, 
2   * grabs and builds the IDL tree
3   */
4 function oilsInitReportBuilder() {
5         oilsInitReports();
6         oilsReportBuilderReset();
7         DOM.oils_rpt_table.onclick = 
8                 function(){hideMe(DOM.oils_rpt_column_editor)};
9         //oilsRptBuildCalendars();
10         oilsDrawRptTree(
11                 function() { 
12                         hideMe(DOM.oils_rpt_tree_loading); 
13                         unHideMe(DOM.oils_rpt_table); 
14                 }
15         );
16 }
17
18 function oilsReportBuilderReset() {
19         var n = (oilsRpt) ? oilsRpt.name : "";
20         oilsRpt = new oilsReport();
21         oilsRpt.name = n;
22         oilsRptDisplaySelector  = DOM.oils_rpt_display_selector;
23         oilsRptFilterSelector   = DOM.oils_rpt_filter_selector;
24         oilsRptAggFilterSelector= DOM.oils_rpt_agg_filter_selector;
25         removeChildren(oilsRptDisplaySelector);
26         removeChildren(oilsRptFilterSelector);
27         removeChildren(oilsRptAggFilterSelector);
28         oilsRptDebug();
29         oilsRptResetParams();
30 }
31
32
33 /*
34 function oilsRptBuildCalendars() {
35         Calendar.setup({
36                 inputField  : "oils_rpt_filter_tform_timestamp_input", // id of the input field
37                 ifFormat    : "%Y-%m-%d", // format of the input field
38                 button      : "oils_rpt_filter_tform_timestamp_cal",  // trigger for the calendar (button ID)
39                 align       : "Tl", // alignment (defaults to "Bl")
40                 singleClick : true
41         });
42         Calendar.setup({
43                 inputField  : "oils_rpt_filter_tform_timestamp_input_2", // id of the input field
44                 ifFormat    : "%Y-%m-%d", // format of the input field
45                 button      : "oils_rpt_filter_tform_timestamp_cal2",  // trigger for the calendar (button ID)
46                 align       : "Tl", // alignment (defaults to "Bl")
47                 singleClick : true
48         });
49 }
50 */
51
52
53 /* adds an item to the display window */
54 function oilsAddRptDisplayItem(path, name, tform, params) {
55         if( ! oilsAddSelectorItem(oilsRptDisplaySelector, path, name) ) 
56                 return;
57
58         /* add the selected columns to the report output */
59         name = (name) ? name : oilsRptPathCol(path);
60         if( !tform ) tform = 'Bare';
61
62         /* add this item to the select blob */
63         var sel = {
64                 relation: oilsRptPathRel(path), 
65                 alias:    name,
66                 column:   { transform: tform, colname: oilsRptPathCol(path) }
67         };
68
69         if( params ) sel.column.params = params;
70         oilsRpt.def.select.push(sel);
71
72         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
73         oilsRptDebug();
74 }
75
76 /* takes a column path and builds a from-clause object for the path */
77 function oilsRptBuildFromClause(path) {
78
79         /* the path is the full path (relation) from the source 
80                 object to the column in question (e.g. au-home_ou-aou-name)*/
81         var parts = path.split(/-/);
82
83         /* the final from clause */
84         var obj = {}; 
85
86         /* reference to the current position in the from clause */
87         var tobj = obj; 
88
89         var newpath = "";
90
91         /* walk the path, fleshing the from clause as we go */
92         for( var i = 0; i < parts.length; i += 2 ) {
93
94                 var cls = parts[i]; /* class name (id) */
95                 var col = parts[i+1]; /* column name */
96
97                 /* a "node" is a class description from the IDL, it 
98                         contains relevant info, plus a list of "fields",
99                         or column objects */
100                 var node = oilsIDL[cls];
101                 var pkey = oilsRptFindField(node, node.pkey);
102
103                 /* a "field" is a parsed version of a column from the IDL,
104                         contains datatype, column name, etc. */
105                 var field = oilsRptFindField(node, col);
106
107                 /* re-construct the path as we go so 
108                         we know what all we've seen thus far */
109                 newpath = (newpath) ? newpath + '-'+ cls : cls;
110
111                 /* extract relevant info */
112                 tobj.table = node.table;
113                 tobj.alias = newpath;
114                 _debug('field type is ' + field.type);
115                 if( i == (parts.length - 2) ) break;
116
117                 /* we still have columns left in the path, keep adding join's */
118                 var path_col = col;
119                 if(field.reltype != 'has_a')
120                         col = pkey.name + '-' + col;
121
122                 tobj.join = {};
123                 tobj = tobj.join;
124
125                 tobj[col] = {};
126                 tobj = tobj[col];
127                 if( field.type == 'link' )
128                         tobj.key = field.key;
129
130                 newpath = newpath + '-'+ path_col;
131         }
132
133         _debug("built 'from' clause: path="+path+"\n"+formatJSON(js2JSON(obj)));
134         return obj;
135 }
136
137
138 /* removes a specific item from the display window */
139 function oilsDelDisplayItem(val) {
140         oilsDelSelectorItem(oilsRptDisplaySelector, val);
141 }
142
143 /* removes selected items from the display window */
144 function oilsDelSelectedDisplayItems() {
145         var list = oilsDelSelectedItems(oilsRptDisplaySelector);
146
147         /* remove the de-selected columns from the report output */
148         oilsRpt.def.select = grep( oilsRpt.def.select, 
149                 function(i) {
150                         for( var j = 0; j < list.length; j++ ) {
151                                 var d = list[j];
152                                 var col = i.column;
153
154                                 /* if this columsn has a transform, 
155                                         it will be an object { tform => column } */
156                                 if( typeof col != 'string' ) 
157                                         for( var c in col ) col = col[c];
158
159                                 /* if this transform requires params, the column 
160                                         will be the first item in the param set array */
161                                 if( typeof col != 'string' ) col = col[0];
162
163                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
164                                         var param = (i.alias) ? i.alias.match(/::PARAM\d*/) : null;
165                                         if( param ) delete oilsRpt.params[param];
166                                         return false;
167                                 }
168                         }
169                         return true;
170                 }
171         );
172
173         if(!oilsRpt.def.select) {
174                 oilsRpt.def.select = [];
175                 oilsReportBuilderReset();
176
177         } else {
178                 for( var j = 0; j < list.length; j++ ) 
179                         /* if there are no items left in the "select", "where", or "having" clauses 
180                                 for the given relation, trim this relation from the "from" clause */
181                         if(     !grep(oilsRpt.def.select,
182                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
183                                 && !grep(oilsRpt.def.where,
184                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
185                                 && !grep(oilsRpt.def.having,
186                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
187                         ) oilsRptPruneFromClause(oilsRptPathRel(list[j]));
188         }
189
190         oilsRptDebug();
191 }
192
193 /* for each item in the path list, remove the associated data
194         from the "from" clause */
195
196 function oilsRptPruneFromClause(relation, node) {
197         _debug("removing relation from 'from' clause " + relation);
198         if(!node) node = oilsRpt.def.from.join;
199         for( var i in node ) {
200                 if( node[i].alias == relation ) {
201                         if( node[i].join ) {
202                                 /* if we have subtrees, don't delete our tree node */
203                                 return false;
204                         } else {
205                                 delete node[i];
206                                 return true;
207                         } 
208                 } else {
209                         if( node[i].join ) {
210                                 if( oilsRptPruneFromClause(relation, node[i].join ) ) {
211                                         if(oilsRptObjectKeys(node[i].join).length == 0) {
212                                                 delete node[i].join;
213                                                 /* if there are no items in the select clause with a relation matching
214                                                         this nodes alias, we can safely remove this node from the tree */
215                                                 if(!grep(oilsRpt.def.select,function(r){return (r.relation==node[i].alias)}))
216                                                         delete node[i];
217                                                 return true;
218                                         }
219                                 }
220                         }
221                 }
222         }
223         return false;
224 }
225
226 /* adds an item to the display window */
227 function oilsAddRptFilterItem(val) {
228         oilsAddSelectorItem(oilsRptFilterSelector, val);
229 }
230
231 /* removes a specific item from the display window */
232 function oilsDelFilterItem(val) {
233         oilsDelSelectorItem(oilsRptFilterSelector, val);
234 }
235
236 /* removes selected items from the display window */
237 function oilsDelSelectedFilterItems() {
238         var list = oilsDelSelectedItems(oilsRptFilterSelector);
239
240         /* remove the de-selected columns from the report output */
241         oilsRpt.def.where = grep( oilsRpt.def.where, 
242                 function(i) {
243                         for( var j = 0; j < list.length; j++ ) {
244                                 var d = list[j];
245                                 var col = i.column;
246
247                                 /* if this columsn has a transform, 
248                                         it will be an object { tform => column } */
249                                 if( typeof col != 'string' ) 
250                                         for( var c in col ) col = col[c];
251
252                                 /* if this transform requires params, the column 
253                                         will be the first item in the param set array */
254                                 if( typeof col != 'string' ) col = col[0];
255
256                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
257                                         var param = (i.alias) ? i.alias.match(/::PARAM\d*/) : null;
258                                         if( param ) delete oilsRpt.params[param];
259                                         return false;
260                                 }
261                         }
262                         return true;
263                 }
264         );
265
266         if(!oilsRpt.def.where) {
267                 oilsRpt.def.where = [];
268                 oilsReportBuilderReset();
269
270         } else {
271                 for( var j = 0; j < list.length; j++ ) 
272                         /* if there are no items left in the "select", "where", or "having" clauses 
273                                 for the given relation, trim this relation from the "from" clause */
274                         if(     !grep(oilsRpt.def.select,
275                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
276                                 && !grep(oilsRpt.def.where,
277                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
278                                 && !grep(oilsRpt.def.having,
279                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
280                         ) oilsRptPruneFromClause(oilsRptPathRel(list[j]));
281         }
282
283         oilsRptDebug();
284 }
285
286 /* adds an item to the display window */
287 function oilsAddRptAggFilterItem(val) {
288         oilsAddSelectorItem(oilsRptAggFilterSelector, val);
289 }
290
291 /* removes a specific item from the display window */
292 function oilsDelAggFilterItem(val) {
293         oilsDelSelectorItem(oilsRptAggFilterSelector, val);
294 }
295
296 /* removes selected items from the display window */
297 function oilsDelSelectedAggFilterItems() {
298         var list = oilsDelSelectedItems(oilsRptAggFilterSelector);
299
300         /* remove the de-selected columns from the report output */
301         oilsRpt.def.having = grep( oilsRpt.def.having, 
302                 function(i) {
303                         for( var j = 0; j < list.length; j++ ) {
304                                 var d = list[j];
305                                 var col = i.column;
306
307                                 /* if this columsn has a transform, 
308                                         it will be an object { tform => column } */
309                                 if( typeof col != 'string' ) 
310                                         for( var c in col ) col = col[c];
311
312                                 /* if this transform requires params, the column 
313                                         will be the first item in the param set array */
314                                 if( typeof col != 'string' ) col = col[0];
315
316                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
317                                         var param = (i.alias) ? i.alias.match(/::PARAM\d*/) : null;
318                                         if( param ) delete oilsRpt.params[param];
319                                         return false;
320                                 }
321                         }
322                         return true;
323                 }
324         );
325
326         if(!oilsRpt.def.having) {
327                 oilsRpt.def.having = [];
328                 oilsReportBuilderReset();
329
330         } else {
331                 for( var j = 0; j < list.length; j++ ) 
332                         /* if there are no items left in the "select", "where", or "having" clauses 
333                                 for the given relation, trim this relation from the "from" clause */
334                         if(     !grep(oilsRpt.def.select,
335                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
336                                 && !grep(oilsRpt.def.where,
337                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
338                                 && !grep(oilsRpt.def.having,
339                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })
340                         ) oilsRptPruneFromClause(oilsRptPathRel(list[j]));
341         }
342
343         oilsRptDebug();
344 }
345
346
347 /* adds an item to the display window */
348 function oilsAddSelectorItem(sel, val, name) {
349         name = (name) ? name : oilsRptMakeLabel(val);
350         _debug("adding selector item "+name+' = ' +val);
351         for( var i = 0; i < sel.options.length; i++ ) {
352                 var opt = sel.options[i];
353                 if( opt.value == val ) return false;
354         }
355         insertSelectorVal( sel, -1, name, val );
356         return true;
357 }
358
359
360 /* removes a specific item from the display window */
361 function oilsDelSelectorItem(sel, val) {
362         _debug("deleting selector item "+val);
363         var opts = sel.options;
364         for( var i = 0; i < opts.length; i++ ) {
365                 var opt = opts[i];
366                 if( opt.value == val )  {
367                         if( i == opts.length - 1 ) 
368                                 opts[i] = null;
369                         else opts[i] = opts[i+1];
370                         return;
371                 }
372         }
373 }
374
375 /* removes selected items from the display window */
376 function oilsDelSelectedItems(sel) {
377         var list = getSelectedList(sel);
378         for( var i = 0; i < list.length; i++ ) 
379                 oilsDelSelectorItem(sel, list[i]);
380         return list;
381 }
382
383
384 /* hides the different field editor tabs */
385 function oilsRptHideEditorDivs() {
386         hideMe(DOM.oils_rpt_tform_div);
387         hideMe(DOM.oils_rpt_filter_div);
388         hideMe(DOM.oils_rpt_agg_filter_div);
389 }
390
391
392 /**
393   This draws the 3-tabbed window containing the transform,
394   filter, and aggregate filter picker window
395   */
396 function oilsRptDrawDataWindow(path) {
397         var col = oilsRptPathCol(path);
398         var cls = oilsRptPathClass(path);
399         var field = oilsRptFindField(oilsIDL[cls], col);
400
401         appendClear(DOM.oils_rpt_editor_window_label, text(oilsRptMakeLabel(path)));
402         appendClear(DOM.oils_rpt_editor_window_datatype, text(field.datatype));
403
404         _debug("setting update data window for column "+col+' on class '+cls);
405
406         var div = DOM.oils_rpt_column_editor;
407         /* set a preliminary top position so the page won't bounce around */
408         div.setAttribute('style','top:'+oilsMouseX+'px');
409
410         /* unhide the div so we can determine the dimensions */
411         unHideMe(div);
412
413         /* don't let them see the floating div until the position is fully determined */
414         div.style.visibility='hidden'; 
415
416         oilsRptDrawTransformWindow(path, col, cls, field);
417         oilsRptDrawFilterWindow(path, col, cls, field);
418
419         //oilsRptSetFilters(field.datatype);
420
421         //oilsRptDoFilterWidgets();
422
423         //DOM.oils_rpt_filter_tform_selector.onchange = oilsRptDoFilterWidgets;
424
425         buildFloatingDiv(div, 600);
426
427         /* now let them see it */
428         div.style.visibility='visible';
429
430         oilsRptSetDataWindowActions(div);
431 }
432
433
434 function oilsRptSetDataWindowActions(div) {
435         /* give the tab links behavior */
436         DOM.oils_rpt_tform_tab.onclick = 
437                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_tform_div)};
438         DOM.oils_rpt_filter_tab.onclick = 
439                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_filter_div)};
440         DOM.oils_rpt_agg_filter_tab.onclick = 
441                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_agg_filter_div)};
442
443         DOM.oils_rpt_tform_tab.onclick();
444         DOM.oils_rpt_column_editor_close_button.onclick = function(){hideMe(div);};
445 }
446
447
448 function oilsRptDrawFilterWindow(path, col, cls, field) {
449         oilsRptCurrentFilterTform = new oilsRptTFormManager(DOM.oils_rpt_filter_tform_table);
450         oilsRptCurrentFilterTform.build(field.datatype, false, true);
451         oilsRptCurrentFilterOpManager = new oilsRptOpManager(DOM.oils_rpt_filter_op_table);
452 }
453
454
455 /* draws the transform window */
456 function oilsRptDrawTransformWindow(path, col, cls, field) {
457         DOM.oils_rpt_tform_label_input.value = oilsRptMakeLabel(path);
458         var dtype = field.datatype;
459
460         DOM.oils_rpt_tform_submit.onclick = 
461                 function(){ 
462                         /*
463                         var tform = oilsRptGetTform(dtype);
464                         _debug('found tform: ' + js2JSON(tform));
465                         var params = getRptTformParams(dtype, tform);
466                         _debug('found tform params: ' + js2JSON(params));
467                         tform = (tform == 'raw') ? null : tform;
468                         */
469
470                         var tform = oilsRptCurrentTform.getCurrentTForm();
471                         oilsAddRptDisplayItem(path, DOM.oils_rpt_tform_label_input.value, tform.value, tform.params ) 
472                 };
473
474
475         DOM.oils_rpt_tform_label_input.focus();
476         DOM.oils_rpt_tform_label_input.select();
477
478         oilsRptCurrentTform = new oilsRptTFormManager(DOM.oils_rpt_tform_table);
479         oilsRptCurrentTform.build(dtype, true, true);
480
481         /*
482         oilsRptHideTformFields();
483         oilsRptUnHideTformFields(dtype);
484         */
485
486         _debug("Building transiform window for datatype "+dtype);
487
488         /*
489         unHideMe($('oils_rpt_tform_'+dtype+'_div'));
490         $('oils_rpt_tform_all_raw').checked = true;
491         */
492 }
493
494 /*
495 function oilsRptHideTformFields() {
496         var rows = DOM.oils_rpt_tform_tbody.childNodes;
497         for( var i = 0; i < rows.length; i++ )
498                 if( rows[i] && rows[i].nodeType == 1 )
499                         hideMe(rows[i]);
500 }
501
502 function oilsRptUnHideTformFields(dtype) {
503         var rows = DOM.oils_rpt_tform_tbody.childNodes;
504         for( var i = 0; i < rows.length; i++ ) {
505                 var row = rows[i]
506                 if( row && row.nodeType == 1 && 
507                         (row.getAttribute('datatype')=='all' 
508                                 || row.getAttribute('datatype') == dtype)) {
509                         unHideMe(row);
510                 }
511         }
512 }
513
514
515 function oilsRptGetTform(datatype) {
516         for( var i in oilsRptTransforms[datatype] ) 
517                 if( $('oils_rpt_tform_'+datatype+'_'+oilsRptTransforms[datatype][i]).checked )
518                         return oilsRptTransforms[datatype][i];
519         for( var i in oilsRptTransforms.all ) 
520                 if( $('oils_rpt_tform_all_'+oilsRptTransforms.all[i]).checked )
521                         return oilsRptTransforms.all[i];
522         return null;
523 }
524 */
525
526
527 /*
528 function getRptTformParams(type, tform) {
529         switch(type) {
530                 case 'string' :
531                         switch(tform) {
532                                 case 'substring' :
533                                         return [
534                                                 DOM.oils_rpt_tform_string_substring_offset.value, 
535                                                 DOM.oils_rpt_tform_string_substring_length.value];
536                         }
537         }
538 }
539 */
540
541
542 /* given a transform selector, this displays the appropriate 
543         transforms for the given datatype.
544         if aggregate is true, is displays the aggregate transforms */
545 /*
546 function oilsRptSetTransforms(sel, dtype, show_agg, show_noagg) {
547         for( var i = 0; i < sel.options.length; i++ ) {
548                 var opt = sel.options[i];
549                 var t = opt.getAttribute('datatype');
550                 if( t && t != dtype ){
551                         hideMe(opt);
552                 } else {
553                         var ag = opt.getAttribute('aggregate');
554                         if( ag && show_agg )
555                                 unHideMe(opt);
556                         else if( ag && ! show_agg )
557                                 hideMe(opt)
558                         else if( !ag && show_noagg )
559                                 unHideMe(opt);
560                         else
561                                 hideMe(opt);
562                 }
563         }
564 }
565 */
566
567
568 /* displays the correct filter-transforms for the given datatype */
569 /*
570 function oilsRptSetFilters(dtype) {
571
572         DOM.oils_rpt_filter_submit.onclick = function() {
573                 var data = oilsRptDoFilterWidgets();
574                 alert(js2JSON(data));
575         }
576
577         var sel = DOM.oils_rpt_filter_tform_selector;
578         for( var i = 0; i < sel.options.length; i++ ) {
579                 var opt = sel.options[i];
580                 _debug(opt.getAttribute('op'));
581                 var t = opt.getAttribute('datatype');
582                 if( t && t != dtype ) hideMe(opt);
583                 else unHideMe(opt);
584         }
585 }
586 */
587
588 /* hides all of the filter widgets */
589 function oilsRptHideFilterWidgets(node) {
590         if(!node)
591                 node = DOM.oils_rpt_filter_tform_widget_td;
592         if( node.nodeType != 1 ) return;
593         if( node.getAttribute('widget') ) {
594                 hideMe(node);
595         } else {
596                 var cs = node.childNodes;
597                 for( var i = 0; cs && i < cs.length; i++ )
598                         oilsRptHideFilterWidgets(cs[i]);
599         }
600 }
601
602 /* what does this need to do? */
603 function oilsRptSetFilterOpActions() {
604 }
605
606
607
608 /* hides/unhides the appropriate widgets and returns the parameter
609         array appropriate for the selected widget */
610 function oilsRptDoFilterWidgets() {
611         filter = getSelectorVal(DOM.oils_rpt_filter_tform_selector);
612         oilsRptHideFilterWidgets();
613         var op = null;
614         var tform = null;
615         var params = null;
616
617         switch(filter) {
618                 
619                 /* generic transforms */
620                 case 'equals':
621                         if(!op) op = 'equals';
622                 case 'like':
623                         if(!op) op = 'like';
624                 case 'ilike':
625                         if(!op) op = 'ilike';
626                 case 'gt':
627                         if(!op) op = '>';
628                 case 'gte':
629                         if(!op) op = '>=';
630                 case 'lt':
631                         if(!op) op = '<';
632                 case 'lte':
633                         if(!op) op = '<=';
634                 case 'in':
635                         if(!op) op = 'in';
636                 case 'not_in':
637                         if(!op) op = 'not in';
638                 case 'between':
639                         if(!op) op = 'between';
640                 case 'not_between':
641                         if(!op) op = 'not between';
642                         unHideMe(DOM.oils_rpt_filter_tform_input);      
643                         params = [DOM.oils_rpt_filter_tform_input.value];
644                         break;
645
646                 /* timestamp transforms */
647                 case 'date_between':
648                         if(!op) op = 'between';
649                 case 'date_not_between':
650                         if(!op) op = 'not between';
651                         tform = 'date';
652                         var d = new Date();
653                         unHideMe(DOM.oils_rpt_filter_tform_date_1);
654                         unHideMe(DOM.oils_rpt_filter_tform_date_2);
655                         unHideMe(DOM.oils_rpt_filter_tform_date_hint);
656                         DOM.oils_rpt_filter_tform_date_1.value = mkYearMonDay();
657                         DOM.oils_rpt_filter_tform_date_2.value = mkYearMonDay();
658                         params = [
659                                 DOM.oils_rpt_filter_tform_date_1.value,
660                                 DOM.oils_rpt_filter_tform_date_2.value
661                         ];
662                         break;
663
664                 case 'dow_between':
665                         op = 'between';
666                         if(!tform) tform = 'dow';
667                 case 'dow_not_between':
668                         if(!op) op = 'not between';
669                         if(!tform) tform = 'dow';
670                         break;
671
672                 case 'dom_between':
673                         op = 'between';
674                         if(!tform) tform = 'dom';
675                 case 'dom_not_between':
676                         if(!op) op = 'not between';
677                         if(!tform) tform = 'dom';
678                         break;
679
680                 case 'month_between':
681                         op = 'between';
682                         if(!tform) tform = 'moy';
683                 case 'month_not_between':
684                         if(!op) op = 'not between';
685                         if(!tform) tform = 'moy';
686                         break;
687
688                 case 'quarter_between':
689                         op = 'between';
690                         if(!tform) tform = 'qoy';
691                 case 'quarter_not_between':
692                         if(!op) op = 'not between';
693                         if(!tform) tform = 'qoy';
694                         break;
695
696                 case 'year_between':
697                         if(!op) op = 'between';
698                         if(!tform) tform = 'year_trunc';
699                 case 'year_not_between':
700                         if(!op) op = 'not between';
701                         if(!tform) tform = 'year_trunc';
702                         break;
703
704                 case 'age_between':
705                         if(!op) op = 'between';
706                         if(!tform) tform = 'age';
707                 case 'age_not_between':
708                         if(!op) op = 'not between';
709                         if(!tform) tform = 'age';
710                         break;
711
712                 /* string transforms */
713                 case 'substring':
714                         if(!tform) tform = 'substring';
715                         break;
716
717                 case 'lower':
718                         if(!op) op = '';
719                         if(!tform) tform = 'dow';
720
721                 case 'upper':
722                         if(!op) op = '';
723                         if(!tform) tform = 'dow';
724
725                 /* numeric transforms */
726                 case 'round':
727                         if(!op) op = '';
728                         if(!tform) tform = 'dow';
729
730                 case 'int':
731                         if(!op) op = '';
732                         if(!tform) tform = 'dow';
733         }
734
735         return { op : op, params : params, tform : tform };
736 }
737
738
739
740