6994ac8c09020c7b365684127148ccf4472b92db
[app.Builder.js] / Builder / Provider / Database / generate.js
1 //<script type="text/javascript">
2
3 /**
4  * 
5  * Let's see if libgda can be used to generate our Readers for roo...
6  * 
7  * Concept - conect to database..
8  * 
9  * list tables
10  * 
11  * extra schemas..
12  * 
13  * write readers..
14  * 
15  * usage: seed generate.js  '{"DB_NAME":"XXX","USERNAME":"YYY","PASSWORD":"ZZZ","INI":"/path/to/mydb.ini"}'
16  * 
17  */
18 Gda  = imports.gi.Gda;
19 GObject = imports.gi.GObject;
20
21 GLib = imports.gi.GLib;
22
23 console = imports['../../../console.js'];
24 File = imports['../../../File.js'].File;
25 Gda.init();
26
27 var prov = Gda.Config.list_providers ();
28 //print(prov.dump_as_string());
29 var args = Array.prototype.slice.call(Seed.argv);
30 args.shift();args.shift();// remove first 2
31 print(args.length );
32 if (args.length < 1) {
33     var sample = {
34         DB_NAME : "XXX",
35         USERNAME : "YYY",
36         PASSWORD: "ZZZ",
37         INI : "/path/to/mydb.ini",
38     }
39     print("Usage : seed generate.js  '" + JSON.stringify(sample) +"'");
40     Seed.quit();
41 }
42 var cfg = JSON.parse(args[0]);
43
44 var   cnc = Gda.Connection.open_from_string ("MySQL", "DB_NAME=" + cfg.DB_NAME, 
45                                               "USERNAME=" + cfg.USERNAME + ';PASSWORD=' + cfg.PASSWORD,
46                                               Gda.ConnectionOptions.NONE, null);
47
48
49
50                                               
51
52  
53 Gda.DataSelect.prototype.fetchAll = function()
54 {
55     var cols = [];
56     
57     for (var i =0;i < this.get_n_columns(); i++) {
58         cols.push(this.get_column_name(i));
59     }
60     //print(JSON.stringify(cols, null,4));
61     var iter = this.create_iter();
62     var res = [];
63     //print(this.get_n_rows());
64     var _this = this;
65     for (var r = 0; r < this.get_n_rows(); r++) {
66         
67         // single clo..
68         //print("GOT ROW");
69         if (cols.length == 1) {
70             res.push(this.get_value_at(0,r).get_string());
71             continue;
72         }
73         var add = { };
74         
75         cols.forEach(function(n,i) {
76             var val = _this.get_value_at(i,r);
77             var type = GObject.type_name(val.g_type) ;
78             var vs = ['GdaBinary', 'GdaBlob' ].indexOf(type) > -1 ? val.value.to_string(1024) : val.value;
79             //print(n + " : TYPE: " + GObject.type_name(val.g_type) + " : " + vs);
80             //print (n + '=' + iter.get_value_at(i).value);
81             add[n] = vs;
82         });
83         
84         res.push(add);
85         
86     }
87     return res;
88
89 }
90
91 var map = {
92     'date' : 'date',
93     'datetime' : 'string',
94     'int' : 'int',
95     'bigint' : 'int',
96     'char' : 'int',
97     'tinyint' : 'int',
98     'decimal' : 'float',
99     'float' : 'float',
100     'varchar' : 'string',
101     'text' : 'string',
102     'longtext' : 'string',
103     'mediumtext' : 'string',
104     'enum' : 'string',
105     'timestamp' : 'number',
106     'blob' => 'text',
107     
108 }
109
110 var ini = { }
111
112 function readIni(fn)
113 {
114     var key_file = new GLib.KeyFile.c_new();
115     if (!key_file.load_from_file (fn , GLib.KeyFileFlags.NONE )) {
116         return;
117     }
118    
119     var groups = key_file.get_groups();
120     groups.forEach(function(g) {
121         ini[g] = {}
122            
123         var keys = key_file.get_keys(g);
124         keys.forEach(function(k) {
125             ini[g][k] = key_file.get_value(g,k);
126         })
127     })
128     
129 }
130 if (File.isFile(cfg.INI)) {
131     if (cfg.INI.match(/links\.ini$/)) {
132         readIni(cfg.INI);
133     } else {
134         readIni(cfg.INI.replace(/\.ini$/, ".links.ini"));
135     }
136 }
137
138 if (File.isDirectory(cfg.INI)) {
139         
140
141     //--- load ini files..
142     // this is very specific.
143     
144     var dirs = File.list( cfg.INI + '/Pman').filter( 
145         function(e) { 
146             if (!File.isDirectory(cfg.INI + '/Pman/' + e + '/DataObjects')) {
147                 return false;
148             }
149             return true;
150         }
151     );
152     
153      
154     dirs.forEach(function(d) {
155         // this currently misses the web.*/Pman/XXXX/DataObjects..
156         var path = cfg.INI + '/Pman/' + d + '/DataObjects';
157          
158         if (!File.isDirectory(path)) {
159             return; //skip
160         }
161         var inis = File.list(path).filter(
162             function(e) { return e.match(/\.links\.ini$/); }
163         );
164         if (!inis.length) {
165             return;
166         }
167         
168         inis.forEach(function(i) {
169             readIni(path + '/' + i); 
170             
171         })
172  
173     });
174     // look at web.XXXX/Pman/XXX/DataObjects/*.ini
175     var inis = File.list(cfg.INI).filter(
176         function(e) { return e.match(/\.links\.ini$/); }
177     )
178     
179      inis.forEach(function(i) {
180         readIni(path + '/' + i); 
181         
182     })
183     
184     
185 }
186 print(JSON.stringify(ini, null,4));
187  //console.dump(ini);
188
189
190  //Seed.quit();
191
192 //GLib.key_file_load_from_file (key_file, String file, KeyFileFlags flags) : Boolean
193
194
195
196  
197
198 var tables = Gda.execute_select_command(cnc, "SHOW TABLES").fetchAll();
199 var readers = [];
200 tables.forEach(function(table) {
201     //print(table);
202     var schema = Gda.execute_select_command(cnc, "DESCRIBE `" + table+'`').fetchAll();
203     var reader = []; 
204     var colmodel = []; 
205     var combofields= [ { name : 'id', type: 'int' } ]; // technically the primary key..
206          
207     var form = {}
208        
209     var firstTxtCol = '';
210     
211     print(JSON.stringify(schema, null,4));
212     
213     schema.forEach(function(e)  {
214         var type = e.Type.match(/([^(]+)\(([^\)]+)\)/);
215         var row  = { }; 
216         if (type) {
217             e.Type = type[1];
218             e.Size = type[2];
219         }
220         
221         
222         
223         row.name = e.Field;
224         
225         
226         if (typeof(map[e.Type]) == 'undefined') {
227            console.dump(e);
228            throw {
229                 name: "ArgumentError", 
230                 message: "Unknown mapping for type : " + e.Type
231             };
232         }
233         row.type = map[e.Type];
234         
235         if (row.type == 'string' && !firstTxtCol.length) {
236             firstTxtCol = row.name;
237         }
238         
239         if (row.type == 'date') {
240             row.dateFormat = 'Y-m-d';
241         }
242         reader.push(row);
243         
244         if (combofields.length == 1 && row.type == 'string') {
245             combofields.push(row);
246         }
247         
248         
249         var title = row.name.replace(/_id/, '').replace(/_/g, ' ');
250         title  = title[0].toUpperCase() + title.substring(1);
251         
252         colmodel.push({
253             "xtype": "ColumnModel",
254             "header": title,
255             "width":  row.type == 'string' ? 200 : 75,
256             "dataIndex": row.name,
257             "|renderer": row.type != 'date' ? 
258                     "function(v) { return String.format('{0}', v); }" :
259                     "function(v) { return String.format('{0}', v ? v.format('d/M/Y') : ''); }" , // special for date
260             "|xns": "Roo.grid",
261             "*prop": "colModel[]"
262         });
263         var xtype = 'TextField';
264         if (row.type == 'number') {
265             xtype = 'NumberField';
266         }
267         if (row.type == 'date') {
268             xtype = 'DateField';
269         }
270         if (e.Type == 'text') {
271             xtype = 'TextArea';
272         }
273         if (e.name == 'id') {
274             xtype = 'Hidden';
275         }
276         // what about booleans.. -> checkboxes..
277         
278         
279         
280         form[row.name] = {
281             fieldLabel : title,
282             name : row.name,
283             width : row.type == 'string' ? 200 : 75,
284             '|xns' : 'Roo.form',
285             xtype : xtype
286         }
287         if (xtype == 'TextArea') {
288             form[row.name].height = 100;
289         }
290         
291         
292     });
293     
294     var combo = {
295         '|xns' : 'Roo.form',
296         xtype: 'ComboBox',
297         allowBlank : 'false',
298         editable : 'false',
299         emptyText : 'Select ' + table,
300         forceSelection : true,
301         listWidth : 400,
302         loadingText: 'Searching...',
303         minChars : 2,
304         pageSize : 20,
305         qtip: 'Select ' + table,
306         selectOnFocus: true,
307         triggerAction : 'all',
308         typeAhead: true,
309         
310         width: 300,
311         
312         
313         
314         tpl : '<div class="x-grid-cell-text x-btn button"><b>{name}</b> </div>', // SET WHEN USED
315         queryParam : '',// SET WHEN USED
316         fieldLabel : table,  // SET WHEN USED
317         valueField : 'id',
318         displayField : '', // SET WHEN USED eg. project_id_name
319         hiddenName : '', // SET WHEN USED eg. project_id
320         name : '', // SET WHEN USED eg. project_id_name
321         items : [
322             {
323                     
324                 '*prop' : 'store',
325                 'xtype' : 'Store',
326                 '|xns' : 'Roo.data',
327                 listeners : {
328                     '|beforeload' : 'function (_self, o)' +
329                     "{\n" +
330                     "    o.params = o.params || {};\n" +
331                     "    // set more here\n" +
332                     "}\n"
333                 },
334                 items : [
335                     {
336                         '*prop' : 'proxy',
337                         'xtype' : 'HttpProxy',
338                         'method' : 'GET',
339                         '|xns' : 'Roo.data',
340                         '|url' : "baseURL + '/Roo/" + table + ".php'",
341                     },
342                     
343                     {
344                         '*prop' : 'reader',
345                         'xtype' : 'JsonReader',
346                         '|xns' : 'Roo.data',
347                         'id' : 'id',
348                         'root' : 'data',
349                         'totalProperty' : 'total',
350                         '|fields' : JSON.stringify(combofields)
351                         
352                     }
353                 ]
354             }
355         ]
356     }
357     
358     
359     
360     
361     //print(JSON.stringify(reader,null,4));
362     readers.push({
363         table : table ,
364         combo : combo,
365         combofields : combofields,
366         reader :  reader,
367         oreader : JSON.parse(JSON.stringify(reader)), // dupe it..
368         colmodel : colmodel,
369         firstTxtCol : firstTxtCol,
370         form : form
371     });
372     
373     //console.dump(schema );
374     
375      
376 });
377
378
379
380 // merge in the linked tables..
381 readers.forEach(function(reader) {
382     if (typeof(ini[reader.table]) == 'undefined') {
383      
384         return;
385     }
386     print("OVERLAY - " + reader.table);
387     // we have a map..
388     for (var col in ini[reader.table]) {
389         var kv = ini[reader.table][col].split(':');
390         var add = readers.filter(function(r) { return r.table == kv[0] })[0];
391         
392         // merge in data (eg. project_id => project_id_*****
393      
394         add.oreader.forEach(function(or) {
395             reader.reader.push({
396                 name : col + '_' + or.name,
397                 type : or.type
398             });
399         });
400         
401         // col is mapped to something..
402         var combofields = add.combofields;
403         if (add.combofields.length < 2) {
404             continue;
405         }
406         if (typeof(reader.form[col]) == 'undefined') {
407             print("missing linked column " + col);
408             continue;
409         }
410         
411         var combofields_name = add.combofields[1].name;
412         var old =   reader.form[col];
413         reader.form[col] = JSON.parse(JSON.stringify(add.combo)); // clone
414         reader.form[col].queryParam  = 'query[' + combofields_name + ']';// SET WHEN USED
415         reader.form[col].fieldLabel = old.fieldLabel;  // SET WHEN USED
416         reader.form[col].hiddenName = old.name; // SET WHEN USED eg. project_id
417         reader.form[col].displayField = combofields_name; // SET WHEN USED eg. project_id
418         reader.form[col].name  = old.name + '_' + combofields_name; // SET WHEN USED eg. project_id_name
419         reader.form[col].tpl = '<div class="x-grid-cell-text x-btn button"><b>{' + combofields_name +'}</b> </div>'; // SET WHEN USED
420         
421              
422     };
423     
424     
425 });
426
427 //readers.forEach(function(reader) {
428 //    delete reader.oreader;
429 //});
430
431  
432
433
434
435 //print(JSON.stringify(readers, null, 4));
436
437 readers.forEach(function(reader) {
438     
439
440     var dir = GLib.get_home_dir() + '/.Builder/Roo.data.JsonReader'; 
441     if (!File.isDirectory(dir)) {
442         File.mkdir(dir);
443     }
444     
445     // READERS
446     print("WRITE: " +  dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json');
447     
448                 
449     var jreader = {
450         '|xns' : 'Roo.data',
451         xtype : "JsonReader",
452         totalProperty : "total",
453         root : "data",
454         '*prop' : "reader",
455         id : 'id', // maybe no..
456         '|fields' :  JSON.stringify(reader.reader, null,4).replace(/"/g,"'")
457     };
458     
459     File.write(
460         dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json',
461         JSON.stringify(jreader, null, 4)
462     )
463     
464     
465     // GRIDS
466     dir = GLib.get_home_dir() + '/.Builder/Roo.GridPanel'; 
467     if (!File.isDirectory(dir)) {
468         File.mkdir(dir);
469     }
470     
471
472     print("WRITE: " +  dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json');
473     
474     File.write(
475         dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json',
476             
477        
478         JSON.stringify({
479             '|xns' : 'Roo',
480             xtype : "GridPanel",
481             "title": reader.table,
482             "fitToframe": true,
483             "fitContainer": true,
484             "tableName": reader.table,
485             "background": true,
486             "listeners": {
487                 "|activate": "function() {\n    _this.panel = this;\n    if (_this.grid) {\n        _this.grid.footer.onClick('first');\n    }\n}"
488             },
489             "items": [
490                 {
491                     "*prop": "grid",
492                     "xtype": "Grid",
493                     "autoExpandColumn": reader.firstTxtCol,
494                     "loadMask": true,
495                     "listeners": {
496                         "|render": "function() \n" +
497                             "{\n" +
498                             "   _this.grid = this; \n" +
499                             "    //_this.dialog = Pman.Dialog.FILL_IN\n" +
500                             "    if (_this.panel.active) {\n" +
501                             "       this.footer.onClick('first');\n" +
502                             "    }\n" +
503                             "}"
504                     },
505                     "|xns": "Roo.grid",
506
507                     "items": [
508                         {
509                             "*prop": "dataSource",
510                             "xtype": "Store",
511                             
512                             "|xns": "Roo.data",
513                             "items": [
514                                 
515                                 {
516                                     "*prop": "proxy",
517                                     "xtype": "HttpProxy",
518                                     "method": "GET",
519                                     "|url": "baseURL + '/Roo/" + reader.table + ".php'",
520                                     "|xns": "Roo.data"
521                                 },
522                                 jreader
523                             ]
524                         },
525                         {
526                             "*prop": "footer",
527                             "xtype": "PagingToolbar",
528                             "pageSize": 25,
529                             "displayInfo": true,
530                             "displayMsg": "Displaying " + reader.table + "{0} - {1} of {2}",
531                             "emptyMsg": "No " + reader.table + " found",
532                             "|xns": "Roo"
533                         },
534                         {
535                             "*prop": "toolbar",
536                             "xtype": "Toolbar",
537                             "|xns": "Roo",
538                             "items": [
539                                 {
540                                     "text": "Add",
541                                     "xtype": "Button",
542                                     "cls": "x-btn-text-icon",
543                                     "|icon": "Roo.rootURL + 'images/default/dd/drop-add.gif'",
544                                     "listeners": {
545                                         "|click": "function()\n"+
546                                             "{\n"+
547                                             "   //yourdialog.show( { id : 0 } , function() {\n"+
548                                             "   //  _this.grid.footer.onClick('first');\n"+
549                                             "   //}); \n"+
550                                             "}\n"
551                                     },
552                                     "|xns": "Roo"
553                                 },
554                                 {
555                                     "text": "Edit",
556                                     "xtype": "Button",
557                                     "cls": "x-btn-text-icon",
558                                     "|icon": "Roo.rootURL + 'images/default/tree/leaf.gif'",
559                                     "listeners": {
560                                         "|click": "function()\n"+
561                                             "{\n"+
562                                             "    var s = _this.grid.getSelectionModel().getSelections();\n"+
563                                             "    if (!s.length || (s.length > 1))  {\n"+
564                                             "        Roo.MessageBox.alert(\"Error\", s.length ? \"Select only one Row\" : \"Select a Row\");\n"+
565                                             "        return;\n"+
566                                             "    }\n"+
567                                             "    \n"+
568                                             "    //_this.dialog.show(s[0].data, function() {\n"+
569                                             "    //    _this.grid.footer.onClick('first');\n"+
570                                             "    //   }); \n"+
571                                             "    \n"+
572                                             "}\n" 
573                                         
574                                     },
575                                     "|xns": "Roo"
576                                 },
577                                 {
578                                     "text": "Delete",
579                                     "cls": "x-btn-text-icon",
580                                     "|icon": "rootURL + '/Pman/templates/images/trash.gif'",
581                                     "xtype": "Button",
582                                     "listeners": {
583                                         "|click": "function()\n"+
584                                             "{\n"+
585                                             "   //Pman.genericDelete(_this, _this.grid.tableName); \n"+
586                                             "}\n"+
587                                             "        "
588                                     },
589                                     "|xns": "Roo"
590                                 }
591                             ]
592                         }, // end toolbar
593                     ].concat( reader.colmodel)
594                 }
595             ]
596             
597             
598         }, null, 4)
599     )
600     
601     /// FORMS..
602     
603     dir = GLib.get_home_dir() + '/.Builder/Roo.form.Form'; 
604     if (!File.isDirectory(dir)) {
605         File.mkdir(dir);
606     }
607     var formElements = [];
608     for (var k in reader.form) {
609         if (k == 'id') { // should really do primary key testing..
610             continue;
611         }
612         formElements.push(reader.form[k]);
613     }
614     formElements.push(reader.form['id']);
615
616     print("WRITE: " +  dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json');
617     
618     File.write(
619         dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json',
620             
621        
622         JSON.stringify({
623             '|xns' : 'Roo.form',
624             xtype : "Form",
625             listeners : {
626                 "|actioncomplete" : "function(_self,action)\n"+
627                     "{\n"+
628                     "    if (action.type == 'setdata') {\n"+
629                     "       //_this.dialog.el.mask(\"Loading\");\n"+
630                     "       //this.load({ method: 'GET', params: { '_id' : _this.data.id }});\n"+
631                     "       return;\n"+
632                     "    }\n"+
633                     "    if (action.type == 'load') {\n"+
634                     "        _this.dialog.el.unmask();\n"+
635                     "        return;\n"+
636                     "    }\n"+
637                     "    if (action.type =='submit') {\n"+
638                     "    \n"+
639                     "        _this.dialog.el.unmask();\n"+
640                     "        _this.dialog.hide();\n"+
641                     "    \n"+
642                     "         if (_this.callback) {\n"+
643                     "            _this.callback.call(_this, _this.form.getValues());\n"+
644                     "         }\n"+
645                     "         _this.form.reset();\n"+
646                     "         return;\n"+
647                     "    }\n"+
648                     "}\n",
649                 
650                 "|rendered" : "function (form)\n"+
651                     "{\n"+
652                     "    _this.form= form;\n"+
653                     "}\n"
654             },
655             method : "POST",
656             style : "margin:10px;",
657             "|url" : "baseURL + '/Roo/" + reader.table + ".php'",
658             items : formElements
659         }, null, 4)
660     );
661             
662             
663    
664    
665    
666      /// COMBO..
667     
668     dir = GLib.get_home_dir() + '/.Builder/Roo.form.ComboBox'; 
669     if (!File.isDirectory(dir)) {
670         File.mkdir(dir);
671     }
672    
673     print("WRITE: " +  dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json');
674     
675     File.write(
676         dir + '/' + cfg.DB_NAME + '_' + reader.table + '.json',
677             
678        
679         JSON.stringify(reader.combo, null, 4)
680     );
681             
682    
683    
684    
685    
686    
687    
688    
689    
690    
691 });              
692
693
694
695