]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_builder.js
more widget work
[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         if(!oilsInitReports()) return false;
6         oilsReportBuilderReset();
7         DOM.oils_rpt_table.onclick = 
8                 function(){hideMe(DOM.oils_rpt_column_editor)};
9         oilsDrawRptTree(
10                 function() { 
11                         hideMe(DOM.oils_rpt_tree_loading); 
12                         unHideMe(DOM.oils_rpt_table); 
13                 }
14         );
15
16         DOM.oils_rpt_builder_save_template.onclick = oilsReportBuilderSave;
17 }
18
19 function oilsReportBuilderReset() {
20         var n = (oilsRpt) ? oilsRpt.name : "";
21         oilsRpt = new oilsReport();
22         oilsRpt.name = n;
23         oilsRptDisplaySelector  = DOM.oils_rpt_display_selector;
24         oilsRptFilterSelector   = DOM.oils_rpt_filter_selector;
25         oilsRptHavingSelector   = DOM.oils_rpt_agg_filter_selector;
26         removeChildren(oilsRptDisplaySelector);
27         removeChildren(oilsRptFilterSelector);
28         removeChildren(oilsRptHavingSelector);
29         //removeChildren(oilsRptOrderBySelector);
30         oilsRptResetParams();
31 }
32
33 function oilsReportBuilderSave() {
34
35         var tmpl = new rt();
36         tmpl.name(DOM.oils_rpt_builder_new_name.value);
37         tmpl.description(DOM.oils_rpt_builder_new_desc.value);
38         tmpl.owner(USER.id());
39         tmpl.folder(new CGI().param('folder'));
40         tmpl.data(js2JSON(oilsRpt.def));
41
42         if(!confirm('Name : '+tmpl.name() + '\nDescription: ' + tmpl.description()+'\nSave Template?'))
43                 return;
44
45         debugFMObject(tmpl);
46         //return; /* XXX */
47
48
49         var req = new Request(OILS_RPT_CREATE_TEMPLATE, SESSION, tmpl);
50         req.callback(
51                 function(r) {
52                         var res = r.getResultObject();
53                         if( res && res != '0' ) {
54                                 oilsRptAlertSuccess();
55                                 _l('oils_rpt.xhtml');
56                         }
57                 }
58         );
59         
60         req.send();
61 }
62
63
64
65 /* adds an item to the display window */
66 function oilsAddRptDisplayItem(path, name, tform, params) {
67         if( ! oilsAddSelectorItem(oilsRptDisplaySelector, path, name) ) 
68                 return;
69
70         /* add the selected columns to the report output */
71         name = (name) ? name : oilsRptPathCol(path);
72         if( !tform ) tform = 'Bare';
73
74         var aggregate = oilsRptGetIsAgg(tform);
75
76         /* add this item to the select blob */
77         var sel = {
78                 relation: hex_md5(oilsRptPathRel(path)), 
79                 path : path,
80                 alias:    name,
81                 column:   { transform: tform, colname: oilsRptPathCol(path) }
82         };
83
84         if( params ) sel.column.params = params;
85
86         if(!oilsRptGetIsAgg(tform)) {
87                 var select = [];
88                 var added = false;
89                 for( var i = 0; i < oilsRpt.def.select.length; i++ ) {
90                         var item = oilsRpt.def.select[i];
91                         if( !added && oilsRptGetIsAgg( item.column.transform ) ) {
92                                 select.push(sel);
93                                 added = true;
94                         }
95                         select.push(item);
96                 }
97                 if(!added) select.push(sel);
98                 oilsRpt.def.select = select;
99         } else {
100                 oilsRpt.def.select.push(sel);
101         }
102
103
104         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
105         oilsRptDebug();
106 }
107
108 function oilsRptGetIsAgg(tform) {
109         return OILS_RPT_TRANSFORMS[tform].aggregate;
110         
111         /* DEPRECATED */
112         var sel = $n(DOM.oils_rpt_tform_table,'selector');
113         for( var i = 0; i < sel.options.length; i++ ) {
114                 var opt = sel.options[i];
115                 if( opt.getAttribute('value') == tform )
116                         return opt.getAttribute('aggregate');
117         }
118 }
119
120 /* takes a column path and builds a from-clause object for the path */
121 function oilsRptBuildFromClause(path) {
122
123         /* the path is the full path (relation) from the source 
124                 object to the column in question (e.g. au-home_ou-aou-name)*/
125         var parts = path.split(/-/);
126
127         /* the final from clause */
128         var obj = {}; 
129
130         /* reference to the current position in the from clause */
131         var tobj = obj; 
132
133         var newpath = "";
134
135         /* walk the path, fleshing the from clause as we go */
136         for( var i = 0; i < parts.length; i += 2 ) {
137
138                 var cls = parts[i]; /* class name (id) */
139                 var col = parts[i+1]; /* column name */
140
141                 /* a "node" is a class description from the IDL, it 
142                         contains relevant info, plus a list of "fields",
143                         or column objects */
144                 var node = oilsIDL[cls];
145                 var pkey = oilsRptFindField(node, node.pkey);
146
147                 /* a "field" is a parsed version of a column from the IDL,
148                         contains datatype, column name, etc. */
149                 var field = oilsRptFindField(node, col);
150
151                 /* re-construct the path as we go so 
152                         we know what all we've seen thus far */
153                 newpath = (newpath) ? newpath + '-'+ cls : cls;
154
155                 /* extract relevant info */
156                 tobj.table = node.table;
157                 tobj.path = newpath;
158                 tobj.alias = hex_md5(newpath);
159
160                 _debug('field type is ' + field.type);
161                 if( i == (parts.length - 2) ) break;
162
163                 /* we still have columns left in the path, keep adding join's */
164                 var path_col = col;
165                 if(field.reltype != 'has_a')
166                         col = pkey.name + '-' + col;
167
168                 tobj.join = {};
169                 tobj = tobj.join;
170
171                 tobj[col] = {};
172                 tobj = tobj[col];
173                 if( field.type == 'link' )
174                         tobj.key = field.key;
175
176                 newpath = newpath + '-'+ path_col;
177         }
178
179         _debug("built 'from' clause: path="+path+"\n"+formatJSON(js2JSON(obj)));
180         return obj;
181 }
182
183
184 /* removes a specific item from the display window */
185 function oilsDelDisplayItem(val) {
186         oilsDelSelectorItem(oilsRptDisplaySelector, val);
187 }
188
189 /* removes selected items from the display window */
190 function oilsDelSelectedDisplayItems() {
191         var list = oilsDelSelectedItems(oilsRptDisplaySelector);
192
193         _debug('deleting list: ' + list);
194
195         /* remove the de-selected columns from the report output */
196         oilsRpt.def.select = grep( oilsRpt.def.select, 
197                 function(i) {
198                         for( var j = 0; j < list.length; j++ ) {
199                                 var d = list[j]; /* path */
200                                 var col = i.column;
201
202                                 _debug('in delete, looking at list = '+d+' : col = ' + 
203                                         col.colname + ' : relation = ' + i.relation + ' : encoded = ' + hex_md5(oilsRptPathRel(d)) );
204
205                                 if( hex_md5(oilsRptPathRel(d)) == i.relation && oilsRptPathCol(d) == col.colname ) {
206                                         return false;
207                                 }
208                         }
209                         return true;
210                 }
211         );
212
213         if(!oilsRpt.def.select) oilsRpt.def.select = [];
214
215         oilsRptPruneFromList(list);
216
217         /*
218         for( var j = 0; j < list.length; j++ ) {
219                 debug('seeing if we can prune from clause with relation = ' + hex_md5(oilsRptPathRel(list[j])));
220                 if(     !grep(oilsRpt.def.select,
221                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
222                         && !grep(oilsRpt.def.where,
223                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
224                         && !grep(oilsRpt.def.having,
225                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
226                 ) {
227                         _debug('pruning from clause');
228                         oilsRptPruneFromClause(oilsRptPathRel(list[j]));
229                 }
230         }
231         */
232
233         oilsRptDebug();
234 }
235
236 function oilsRptPruneFromList(pathlist) {
237
238         for( var j = 0; j < pathlist.length; j++ ) {
239                 /* if there are no items left in the "select", "where", or "having" clauses 
240                         for the given relation, trim this relation from the "from" clause */
241                 var path = pathlist[j];
242                 var encrel = hex_md5(oilsRptPathRel(path));
243
244                 debug('seeing if we can prune from clause with relation = ' + encrel +' : path = ' + path);
245
246                 var func = function(i){ return (i.relation == hex_md5(oilsRptPathRel(path))); };
247
248                 if(     !grep(oilsRpt.def.select, func) && 
249                                 !grep(oilsRpt.def.where, func) && 
250                                 !grep(oilsRpt.def.having, func) ) {
251
252                         oilsRptPruneFromClause(oilsRptPathRel(pathlist[j]));
253                 }
254         }
255 }
256
257
258 /* for each item in the path list, remove the associated data
259         from the "from" clause */
260
261 function oilsRptPruneFromClause(relation, node) {
262         _debug("removing relation from 'from' clause: " + relation);
263         if(!node) node = oilsRpt.def.from.join;
264         if(!node) return false;
265
266         for( var i in node ) {
267                 _debug("from prune looking at node "+node[i].path);
268                 // first, descend into the tree, and prune leaves first
269                 if( node[i].join ) {
270                         oilsRptPruneFromClause(relation, node[i].join); 
271                         if(oilsRptObjectKeys(node[i].join).length == 0) 
272                                 delete node[i].join;
273                 }
274         }
275
276         if(!node.join) {
277
278                 var key = oilsRptObjectKeys(node)[0];
279                 var from_alias = node[key].alias;
280                 var func = function(n){ return (n.relation == from_alias)};
281
282                 _debug("pruning from clause with alias "+ from_alias);
283
284                 if(     !grep(oilsRpt.def.select, func) &&
285                                 !grep(oilsRpt.def.where, func) &&
286                                 !grep(oilsRpt.def.having, func) ) {
287
288                         // if we're at an unused empty leaf, remove it
289                         delete node[i];
290                         return true;
291                 }
292         }
293
294         return false;
295 }
296
297 function oilsRptMkFilterTags(path, tform, filter) {
298         var name = oilsRptMakeLabel(path);
299         if(tform) name += ' ('+tform+')';
300         name += ' "' + filter + '"';
301         var epath = path + ':'+filter+':';
302         if(tform) epath += tform;
303
304         return [ name, epath ];
305 }
306
307
308 function oilsAddRptFilterItem(path, tform, filter) {
309         _debug("Adding filter item for "+path+" tform="+tform+" filter="+filter);
310
311         var name = oilsRptMkFilterTags(path, tform, filter);
312         var epath = name[1];
313         name = name[0];
314
315         if( ! oilsAddSelectorItem(oilsRptFilterSelector, epath, name) )
316                 return;
317
318         var where = {
319                 relation: hex_md5(oilsRptPathRel(path)), 
320                 path : path,
321                 column:   { transform: tform, colname: oilsRptPathCol(path) },
322                 condition : {}
323         };
324         if( filter == 'is' || filter == 'is not' )
325                 where.condition[filter] = null;
326         else where.condition[filter] = oilsRptNextParam();
327
328         switch(tform) {
329                 case 'substring' : where.column.params = oilsRptNextParam();
330         }
331
332         oilsRpt.def.where.push(where);
333         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
334         oilsRptDebug();
335 }
336
337
338 function oilsAddRptHavingItem(path, tform, filter) {
339         _debug("Adding filter item for "+path+" tform="+tform+" filter="+filter);
340
341         var name = oilsRptMkFilterTags(path, tform, filter);
342         var epath = name[1];
343         name = name[0];
344
345         if( ! oilsAddSelectorItem(oilsRptHavingSelector, epath, name) )
346                 return;
347
348         var having = {
349                 relation: hex_md5(oilsRptPathRel(path)), 
350                 path : path,
351                 column:   { transform: tform, colname: oilsRptPathCol(path) },
352                 condition : {}
353         };
354         if( filter == 'is' || filter == 'is not' )
355                 having.condition[filter] = null;
356         else having.condition[filter] = oilsRptNextParam();
357
358         switch(tform) {
359                 case 'substring' : having.column.params = oilsRptNextParam();
360         }
361
362         oilsRpt.def.having.push(having);
363         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
364         oilsRptDebug();
365 }
366
367
368
369 function oilsDelSelectedFilterItems() {
370         _oilsDelSelectedFilterItems('where');
371 }
372 function oilsDelSelectedAggFilterItems() {
373         _oilsDelSelectedFilterItems('having');
374 }
375
376 function _oilsDelSelectedFilterItems(type) {
377
378         /* the values in this list are formed:  <path>:<operation>:<transform> */
379         var list = oilsDelSelectedItems(oilsRptFilterSelector);
380
381         for( var i = 0; i < list.length; i++ ) {
382                 var enc_path = list[i];
383                 var data = oilsRptParseFilterEncPath(enc_path);
384                 oilsRpt.def[type] = grep( 
385                         oilsRpt.def[type],
386                         function(f) { 
387                                 return oilsRptFilterDataMatches( 
388                                         f, data.path, data.operation, data.tform );
389                         }
390                 );
391         }
392
393         if(!oilsRpt.def[type]) oilsRpt.def[type] = [];
394         oilsRptPruneFromList(list);
395         oilsRptDebug();
396 }
397
398 function oilsRptParseFilterEncPath(item) {
399         return {
400                 path:           item.replace(/:.*/,''),
401                 operation:      item.replace(/.*:(.*):.*/,'$1'),
402                 tform:  item.replace(/.*?:.*?:(.*)/,'$1')
403         };
404 }
405
406
407 function oilsRptFilterDataMatches(filter, path, operation, tform) {
408         var rel = hex_md5(oilsRptPathRel(path));
409         var col = oilsRptPathCol(path);
410
411         if(     col == filter.column.colname &&
412                         rel == filter.relation &&       
413                         tform == filter.column.transform &&
414                         operation == oilsRptObjectKeys(filter)[0] ) return true;
415
416         return false;
417 }
418
419 /*
420 function oilsRptFilterGrep(flist, filter) {
421
422         for( var j = 0; j < flist.length; j++ ) {
423
424                 var fil = flist[j];
425                 var col = filter.column;
426                 var frel = hex_md5(oilsRptPathRel(fil.path));
427                 var fcol = oilsRptPathCol(fil.path);
428
429                 var op = oilsRptObjectKeys(filter.condition)[0];
430
431                 if(     frel == filter.relation && 
432                                 fcol == col.colname && 
433                                 fil.operation == op &&
434                                 fil.tform == col.transform ) {
435                                 return false;
436                 }
437         }
438         return true;
439 }
440 */
441
442 /* adds an item to the display window */
443 function oilsAddRptAggFilterItem(val) {
444         oilsAddSelectorItem(oilsRptHavingFilterSelector, val);
445 }
446
447 /* removes a specific item from the display window */
448 function oilsDelAggFilterItem(val) {
449         oilsDelSelectorItem(oilsRptHavingFilterSelector, val);
450 }
451
452
453
454 /*
455 function ___oilsDelSelectedAggFilterItems() {
456         var list = oilsDelSelectedItems(oilsRptHavingFilterSelector);
457         oilsRpt.def.having = grep( oilsRpt.def.having, 
458                 function(i) {
459                         for( var j = 0; j < list.length; j++ ) {
460                                 var d = list[j];
461                                 var col = i.column;
462
463                                 if( typeof col != 'string' ) 
464                                         for( var c in col ) col = col[c];
465
466                                 if( typeof col != 'string' ) col = col[0];
467
468                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
469                                 */
470                                 //      var param = (i.alias) ? i.alias.match(/::P\d*/) : null;
471                                         /*
472                                         if( param ) delete oilsRpt.params[param];
473                                         return false;
474                                 }
475                         }
476                         return true;
477                 }
478         );
479
480         if(!oilsRpt.def.having) oilsRpt.def.having = [];
481         oilsRptPruneFromList(list);
482         oilsRptDebug();
483 }
484 */
485
486
487 /* adds an item to the display window */
488 function oilsAddSelectorItem(sel, val, name) {
489         name = (name) ? name : oilsRptMakeLabel(val);
490         for( var i = 0; i < sel.options.length; i++ ) {
491                 var opt = sel.options[i];
492                 if( opt.value == val ) return false;
493         }
494         insertSelectorVal( sel, -1, name, val );
495         return true;
496 }
497
498
499 /* removes a specific item from the display window */
500 function oilsDelSelectorItem(sel, val) {
501         var opts = sel.options;
502         for( var i = 0; i < opts.length; i++ ) {
503                 var opt = opts[i];
504                 if( opt.value == val )  {
505                         if( i == opts.length - 1 ) 
506                                 opts[i] = null;
507                         else opts[i] = opts[i+1];
508                         return;
509                 }
510         }
511 }
512
513 /* removes selected items from the display window */
514 function oilsDelSelectedItems(sel) {
515         var list = getSelectedList(sel);
516         for( var i = 0; i < list.length; i++ ) 
517                 oilsDelSelectorItem(sel, list[i]);
518         return list;
519 }
520
521
522 /* hides the different field editor tabs */
523 function oilsRptHideEditorDivs() {
524         hideMe(DOM.oils_rpt_tform_div);
525         hideMe(DOM.oils_rpt_filter_div);
526         hideMe(DOM.oils_rpt_agg_filter_div);
527         hideMe(DOM.oils_rpt_order_by_div);
528 }
529
530
531 /**
532   This draws the 3-tabbed window containing the transform,
533   filter, and aggregate filter picker window
534   */
535 function oilsRptDrawDataWindow(path) {
536         var col = oilsRptPathCol(path);
537         var cls = oilsRptPathClass(path);
538         var field = oilsRptFindField(oilsIDL[cls], col);
539
540         appendClear(DOM.oils_rpt_editor_window_label, text(oilsRptMakeLabel(path)));
541         appendClear(DOM.oils_rpt_editor_window_datatype, text(field.datatype));
542
543         _debug("setting update data window for column "+col+' on class '+cls);
544
545         var div = DOM.oils_rpt_column_editor;
546         /* set a preliminary top position so the page won't bounce around */
547         div.setAttribute('style','top:'+oilsMouseX+'px');
548
549         /* unhide the div so we can determine the dimensions */
550         unHideMe(div);
551
552         /* don't let them see the floating div until the position is fully determined */
553         div.style.visibility='hidden'; 
554
555         oilsRptDrawTransformWindow(path, col, cls, field);
556         oilsRptDrawFilterWindow(path, col, cls, field);
557         oilsRptDrawHavingWindow(path, col, cls, field);
558         oilsRptDrawOrderByWindow(path, col, cls, field);
559
560         buildFloatingDiv(div, 600);
561
562         /* now let them see it */
563         div.style.visibility='visible';
564         oilsRptSetDataWindowActions(div);
565 }
566
567
568 function oilsRptSetDataWindowActions(div) {
569         /* give the tab links behavior */
570         DOM.oils_rpt_tform_tab.onclick = 
571                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_tform_div)};
572         DOM.oils_rpt_filter_tab.onclick = 
573                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_filter_div)};
574         DOM.oils_rpt_agg_filter_tab.onclick = 
575                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_agg_filter_div)};
576
577         DOM.oils_rpt_order_by_tab.onclick = 
578                 function(){
579                         oilsRptHideEditorDivs();
580                         oilsRptDrawOrderByWindow();
581                         unHideMe(DOM.oils_rpt_order_by_div);
582                         };
583
584         DOM.oils_rpt_tform_tab.onclick();
585         DOM.oils_rpt_column_editor_close_button.onclick = function(){hideMe(div);};
586 }
587
588
589 function oilsRptDrawFilterWindow(path, col, cls, field) {
590
591         var tformPicker = new oilsRptTformPicker( {     
592                         node : DOM.oils_rpt_filter_tform_table,
593                         datatype : field.datatype,
594                         non_aggregate : true
595                 }
596         );
597
598         var filterPicker = new oilsRptFilterPicker({
599                         node : DOM.oils_rpt_filter_op_table,
600                         datatype : field.datatype
601                 }
602         );
603
604         DOM.oils_rpt_filter_submit.onclick = function() {
605                 oilsAddRptFilterItem(
606                         path, tformPicker.getSelected(), filterPicker.getSelected());
607         }
608 }
609
610
611 function oilsRptDrawHavingWindow(path, col, cls, field) {
612         var tformPicker = new oilsRptTformPicker( {     
613                         node : DOM.oils_rpt_agg_filter_tform_table,
614                         datatype : field.datatype,
615                         aggregate : true
616                 }
617         );
618
619         var filterPicker = new oilsRptFilterPicker({
620                         node : DOM.oils_rpt_agg_filter_op_table,
621                         datatype : field.datatype
622                 }
623         );
624
625         DOM.oils_rpt_agg_filter_submit.onclick = function() {
626                 oilsAddRptHavingItem(
627                         path, tformPicker.getSelected(), filterPicker.getSelected());
628         }
629 }
630
631 /* draws the transform window */
632 function oilsRptDrawTransformWindow(path, col, cls, field) {
633         DOM.oils_rpt_tform_label_input.value = oilsRptMakeLabel(path);
634         var dtype = field.datatype;
635
636         var tformPicker = new oilsRptTformPicker( {     
637                         node : DOM.oils_rpt_tform_table,
638                         datatype : field.datatype,
639                         non_aggregate : true,
640                         aggregate : true
641                 }
642         );
643
644         DOM.oils_rpt_tform_submit.onclick = 
645                 function(){ 
646                         oilsAddRptDisplayItem(path, 
647                                 DOM.oils_rpt_tform_label_input.value, tformPicker.getSelected() );
648                 };
649
650         DOM.oils_rpt_tform_label_input.focus();
651         DOM.oils_rpt_tform_label_input.select();
652
653         _debug("Building transform window for datatype "+dtype);
654 }
655
656
657 //function oilsRptDrawOrderByWindow(path, col, cls, field) {
658 function oilsRptDrawOrderByWindow() {
659         var sel = DOM.oils_rpt_order_by_selector;
660         removeChildren(sel);
661         DOM.oils_rpt_order_by_submit.onclick = function() {
662                 oilsRptAddOrderBy(getSelectorVal(sel));
663         }
664
665         var cols = oilsRpt.def.select;
666         for( var i = 0; i < cols.length; i++ ) {
667                 var obj = cols[i];
668                 insertSelectorVal(sel, -1, obj.alias, obj.path);
669         }
670 }
671
672 function oilsRptAddOrderBy(path) {
673         var rel = hex_md5(oilsRptPathRel(path));
674         var order_by = oilsRpt.def.order_by;
675
676         /* if this item is already in the order by remove it and overwrite it */
677         order_by = grep(oilsRpt.def.order_by, 
678                 function(i) {return (i.path != path)});
679         
680         if(!order_by) order_by = [];
681
682         /* find the column definition in the select blob */
683         var obj = grep(oilsRpt.def.select,
684                 function(i) {return (i.path == path)});
685         
686         if(!obj) return;
687         obj = obj[0];
688         
689         order_by.push({ 
690                 relation : obj.relation, 
691                 column : obj.column,
692                 direction : getSelectorVal(DOM.oils_rpt_order_by_dir)
693         });
694
695         oilsRpt.def.order_by = order_by;
696         oilsRptDebug();
697 }
698
699
700