~launchpad-p-s/sofastatistics/main

« back to all changes in this revision

Viewing changes to make_table.py

  • Committer: Grant Paton-Simpson
  • Date: 2009-05-19 04:21:43 UTC
  • Revision ID: g@ubuntu-20090519042143-p561mbokz3inefvd
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import wx
 
2
import random
 
3
from datetime import datetime
 
4
import pprint
 
5
import sys
 
6
import os
 
7
 
 
8
import demotables
 
9
import dimtables
 
10
import getdata
 
11
import make_table_gui
 
12
import projects
 
13
import rawtables
 
14
import showhtml
 
15
import table_entry
 
16
import tabreports
 
17
import util
 
18
 
 
19
SCRIPT_PATH = util.get_script_path()
 
20
LOCAL_PATH = util.get_local_path()
 
21
HAS_TOTAL = "Total" #doubles as display label
 
22
COL_MEASURES = 0 #indexes in tab type
 
23
ROW_SUMM = 1
 
24
RAW_DISPLAY = 2
 
25
 
 
26
COL_MEASURES_TREE_LBL = "Column measures"
 
27
 
 
28
# NB raw tables don't have measures
 
29
def get_default_measure(tab_type):
 
30
    "Get default measure appropriate for table type"
 
31
    if tab_type == COL_MEASURES: 
 
32
        return dimtables.FREQ
 
33
    elif tab_type == ROW_SUMM:
 
34
        return dimtables.MEAN
 
35
    else:
 
36
        raise Exception, "Only dimension tables have measures"
 
37
    
 
38
def AddClosingScriptCode(f):
 
39
    "Add ending code to script.  Nb leaves open file."
 
40
    f.write("\n\n#" + "-"*50 + "\n")
 
41
    f.write("\nfil.write(tabreports.getHtmlFtr())")
 
42
    f.write("\nfil.close()")
 
43
 
 
44
def getVarItem(var_labels, var_name):
 
45
    return "%s (%s)" % (var_labels.get(var_name, var_name),
 
46
                        var_name)
 
47
 
 
48
def extractVarDets(choice_text):
 
49
    """
 
50
    Extract var_name, var_label from tree item e.g. return "gender"
 
51
        and "Gender" from "Gender (gender)".
 
52
    """
 
53
    if choice_text == COL_MEASURES_TREE_LBL:
 
54
        var_name = choice_text
 
55
        var_label = choice_text
 
56
    else:
 
57
        start_idx = choice_text.index("(") + 1
 
58
        end_idx = choice_text.index(")")
 
59
        var_name = choice_text[start_idx:end_idx]
 
60
        var_label = choice_text[:start_idx - 2]
 
61
    return var_name, var_label
 
62
 
 
63
def GetColDets(coltree, colRoot, var_labels):
 
64
    """
 
65
    Get names and labels of columns actually selected in GUI column tree.
 
66
    Returns col_names, col_labels.
 
67
    """
 
68
    full_col_labels = util.getSubTreeItems(coltree, colRoot)
 
69
    split_col_tree_labels = full_col_labels.split(", ")        
 
70
    col_names = [extractVarDets(x)[0] for x in split_col_tree_labels]
 
71
    col_labels = [var_labels.get(x, x) for x in col_names]
 
72
    return col_names, col_labels
 
73
 
 
74
 
 
75
class MakeTable(object):
 
76
    "Needed to split  modules for managability"
 
77
 
 
78
    # database/ tables (and views)
 
79
    def OnDatabaseSel(self, event):
 
80
        """
 
81
        Reset dbe, database, cursor, tables, table, tables dropdown, 
 
82
            fields, has_unique, and idxs after a database selection.
 
83
        Clear dim areas.
 
84
        """
 
85
        getdata.ResetDataAfterDbSel(self)
 
86
        self.ClearDims()
 
87
                
 
88
    def OnTableSel(self, event):
 
89
        """
 
90
        Reset table, fields, has_unique, and idxs.
 
91
        Clear dim areas.
 
92
        """       
 
93
        getdata.ResetDataAfterTblSel(self)
 
94
        self.ClearDims()
 
95
 
 
96
    # report output
 
97
    def OnButtonReportPath(self, event):
 
98
        "Open dialog and takes the report file selected (if any)"
 
99
        dlgGetFile = wx.FileDialog(self, "Choose a report output file:", 
 
100
            defaultDir=os.path.join(LOCAL_PATH, "reports"), 
 
101
            defaultFile="", 
 
102
            wildcard="HTML files (*.htm)|*.htm|HTML files (*.html)|*.html")
 
103
            #MUST have a parent to enforce modal in Windows
 
104
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
105
            self.fil_report = "%s" % dlgGetFile.GetPath()
 
106
            self.txtReportFile.SetValue(self.fil_report)
 
107
        dlgGetFile.Destroy()
 
108
        
 
109
    #def ReportPathEnterWindow(self, event):
 
110
    #    "Hover over Report Path Browse button"
 
111
    #    self.statusbar.SetStatusText("Select html file for reporting ...")
 
112
    
 
113
    def OnReportFileLostFocus(self, event):
 
114
        "Reset report output file"
 
115
        self.fil_report = self.txtReportFile.GetValue()
 
116
    
 
117
    # script output
 
118
    def OnButtonScriptPath(self, event):
 
119
        "Open dialog and takes the script file selected (if any)"
 
120
        dlgGetFile = wx.FileDialog(self, "Choose a file to export scripts to:", 
 
121
            defaultDir=os.path.join(LOCAL_PATH, "scripts"), 
 
122
            defaultFile="", wildcard="Scripts (*.py)|*.py")
 
123
            #MUST have a parent to enforce modal in Windows
 
124
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
125
            self.fil_script = "%s" % dlgGetFile.GetPath()
 
126
            self.txtScriptFile.SetValue(self.fil_script)
 
127
        dlgGetFile.Destroy()
 
128
    
 
129
    #def ScriptPathEnterWindow(self, event):
 
130
    #    "Hover over Script Path Browse button"
 
131
    #    self.statusbar.SetStatusText("Select output script file ...")
 
132
 
 
133
    def OnScriptFileLostFocus(self, event):
 
134
        "Reset script file"
 
135
        self.fil_script = self.txtScriptFile.GetValue()
 
136
    
 
137
    # label config
 
138
    def OnLabelFileLostFocus(self, event):
 
139
        ""
 
140
        self.UpdateLabels()
 
141
 
 
142
    def OnButtonLabelPath(self, event):
 
143
        "Open dialog and takes the labels file selected (if any)"
 
144
        dlgGetFile = wx.FileDialog(self, "Choose a label config file:", 
 
145
            defaultDir=os.path.join(LOCAL_PATH, "lbls"), 
 
146
            defaultFile="", wildcard="Config files (*.lbls)|*.lbls")
 
147
            #MUST have a parent to enforce modal in Windows
 
148
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
149
            fil_labels = "%s" % dlgGetFile.GetPath()
 
150
            self.txtLabelsFile.SetValue(fil_labels)
 
151
            self.UpdateLabels()
 
152
        dlgGetFile.Destroy()
 
153
        
 
154
    def UpdateLabels(self):
 
155
        "Update all labels, including those already displayed"
 
156
        self.fil_labels = self.txtLabelsFile.GetValue()
 
157
        self.var_labels, self.var_notes, self.val_dics = \
 
158
            projects.GetLabels(self.fil_labels)
 
159
        # update dim trees
 
160
        rowdescendants = util.getTreeCtrlDescendants(self.rowtree, self.rowRoot)
 
161
        self.RefreshDescendants(self.rowtree, rowdescendants)
 
162
        coldescendants = util.getTreeCtrlDescendants(self.coltree, self.colRoot)
 
163
        self.RefreshDescendants(self.coltree, coldescendants)
 
164
        # update demo area
 
165
        self.demo_tab.var_labels = self.var_labels
 
166
        self.demo_tab.val_dics = self.val_dics
 
167
        self.UpdateDemoDisplay()
 
168
    
 
169
    def RefreshDescendants(self, tree, descendants):
 
170
        ""
 
171
        for descendant in descendants:
 
172
            var_name, _ = extractVarDets(tree.GetItemText(descendant))
 
173
            fresh_label = getVarItem(self.var_labels, var_name)
 
174
            tree.SetItemText(descendant, fresh_label)
 
175
 
 
176
    #def LabelPathEnterWindow(self, event):
 
177
    #    "Hover over Label Path Browse button"
 
178
    #    self.statusbar.SetStatusText("Select source of variable " + \
 
179
    #                                 "and value labels ...")
 
180
        
 
181
    # css table style
 
182
    def OnButtonCssPath(self, event):
 
183
        "Open dialog and takes the css file selected (if any)"
 
184
        dlgGetFile = wx.FileDialog(self, "Choose a css table style file:", 
 
185
            defaultDir=os.path.join(LOCAL_PATH, "css"), 
 
186
            defaultFile="", 
 
187
            wildcard="CSS files (*.css)|*.css")
 
188
            #MUST have a parent to enforce modal in Windows
 
189
        if dlgGetFile.ShowModal() == wx.ID_OK:
 
190
            fil_css = "%s" % dlgGetFile.GetPath()
 
191
            self.txtCssFile.SetValue(fil_css)
 
192
            self.UpdateCss()
 
193
        dlgGetFile.Destroy()
 
194
    
 
195
    def UpdateCss(self):
 
196
        "Update css, including for demo table"
 
197
        self.fil_css = self.txtCssFile.GetValue()
 
198
        self.demo_tab.fil_css = self.fil_css
 
199
        self.UpdateDemoDisplay()
 
200
        
 
201
    #def CssPathEnterWindow(self, event):
 
202
    #    "Hover over Css Path Browse button"
 
203
    #    self.statusbar.SetStatusText("Select css table style file for " + \
 
204
    #                                 "reporting ...")
 
205
    
 
206
    def OnCssFileLostFocus(self, event):
 
207
        "Reset css file"
 
208
        self.UpdateCss()
 
209
 
 
210
    # table type
 
211
    def OnTabTypeChange(self, event):
 
212
        "Respond to change of table type"
 
213
        self.tab_type = self.radTabType.GetSelection() #for convenience
 
214
        #delete all row and col vars
 
215
        self.rowtree.DeleteChildren(self.rowRoot)
 
216
        self.coltree.DeleteChildren(self.colRoot)
 
217
        #link to appropriate demo table type
 
218
        titles = ["\"%s\"" % x for x \
 
219
                  in self.txtTitles.GetValue().split("\n")]
 
220
        Subtitles = ["\"%s\"" % x for x \
 
221
                     in self.txtSubtitles.GetValue().split("\n")]
 
222
        if self.tab_type == COL_MEASURES:
 
223
            self.chkTotalsRow.SetValue(False)
 
224
            self.chkFirstAsLabel.SetValue(False)
 
225
            self.EnableOpts(enable=False)
 
226
            self.EnableRowSel(enable=True)
 
227
            self.EnableColButtons()
 
228
            self.demo_tab = demotables.GenDemoTable(txtTitles=self.txtTitles, 
 
229
                             txtSubtitles=self.txtSubtitles,
 
230
                             colRoot=self.colRoot,                               
 
231
                             rowRoot=self.rowRoot, 
 
232
                             rowtree=self.rowtree, 
 
233
                             coltree=self.coltree, 
 
234
                             col_no_vars_item=self.col_no_vars_item, 
 
235
                             var_labels=self.var_labels, 
 
236
                             val_dics=self.val_dics,
 
237
                             fil_css=self.fil_css)
 
238
        elif self.tab_type == ROW_SUMM:
 
239
            self.chkTotalsRow.SetValue(False)
 
240
            self.chkFirstAsLabel.SetValue(False)
 
241
            self.EnableOpts(enable=False)
 
242
            self.EnableRowSel(enable=True)
 
243
            self.EnableColButtons()
 
244
            self.demo_tab = demotables.SummDemoTable(txtTitles=self.txtTitles, 
 
245
                             txtSubtitles=self.txtSubtitles,
 
246
                             colRoot=self.colRoot,                                  
 
247
                             rowRoot=self.rowRoot, 
 
248
                             rowtree=self.rowtree, 
 
249
                             coltree=self.coltree, 
 
250
                             col_no_vars_item=self.col_no_vars_item, 
 
251
                             var_labels=self.var_labels, 
 
252
                             val_dics=self.val_dics,
 
253
                             fil_css=self.fil_css)
 
254
        elif self.tab_type == RAW_DISPLAY:
 
255
            self.EnableOpts(enable=True)
 
256
            self.EnableRowSel(enable=False)
 
257
            self.btnColConf.Disable()
 
258
            self.btnColAddUnder.Disable()
 
259
            self.demo_tab = demotables.DemoRawTable(txtTitles=self.txtTitles, 
 
260
                     txtSubtitles=self.txtSubtitles,                                 
 
261
                     colRoot=self.colRoot, 
 
262
                     coltree=self.coltree, 
 
263
                     flds=self.flds,
 
264
                     var_labels=self.var_labels,
 
265
                     val_dics=self.val_dics,
 
266
                     fil_css=self.fil_css,
 
267
                     chkTotalsRow=self.chkTotalsRow,
 
268
                     chkFirstAsLabel=self.chkFirstAsLabel)
 
269
        #in case they were disabled and then we changed tab type
 
270
        self.UpdateDemoDisplay()
 
271
    
 
272
    def EnableOpts(self, enable=True):
 
273
        "Enable (or disable) options"
 
274
        self.chkTotalsRow.Enable(enable)
 
275
        self.chkFirstAsLabel.Enable(enable)
 
276
        
 
277
    def OnChkTotalsRow(self, event):
 
278
        "Update display as total rows checkbox changes"
 
279
        self.UpdateDemoDisplay()
 
280
 
 
281
    def OnChkFirstAsLabel(self, event):
 
282
        "Update display as first column as label checkbox changes"
 
283
        self.UpdateDemoDisplay()
 
284
                
 
285
    # titles/subtitles
 
286
    def OnTitleChange(self, event):
 
287
        "Update display as titles change"
 
288
        self.UpdateDemoDisplay()
 
289
 
 
290
    def OnSubtitleChange(self, event):
 
291
        "Update display as subtitles change"
 
292
        self.UpdateDemoDisplay()
 
293
 
 
294
    # run    
 
295
    def OnButtonRun(self, event):
 
296
        """
 
297
        Generate script to special location (INT_SCRIPT_PATH), 
 
298
            run script putting output in special location 
 
299
            (INT_REPORT_PATH), display html output.
 
300
        """
 
301
        run_ok, missing_dim, has_rows, has_cols = self.TableConfigOK()
 
302
        if run_ok:
 
303
            # hourglass cursor
 
304
            curs = wx.StockCursor(wx.CURSOR_WAIT)
 
305
            self.SetCursor(curs)
 
306
            #self.statusbar.SetStatusText("Please wait for report " + \
 
307
            #                             "to be produced")   
 
308
            # generate script
 
309
            f = file(projects.INT_SCRIPT_PATH, "w")
 
310
            self.InsertPrelimCode(fil=f, fil_report=projects.INT_REPORT_PATH)
 
311
            self.AppendExportedScript(f, has_rows, has_cols)
 
312
            AddClosingScriptCode(f)
 
313
            f.close()
 
314
            # run script
 
315
            f = file(projects.INT_SCRIPT_PATH, "r")
 
316
            script = f.read()
 
317
            f.close()
 
318
            exec(script)
 
319
            f = file(projects.INT_REPORT_PATH, "r")
 
320
            strContent = f.read()
 
321
            f.close()
 
322
            # Return to normal cursor
 
323
            curs = wx.StockCursor(wx.CURSOR_ARROW)
 
324
            self.SetCursor(curs)
 
325
            #self.statusbar.SetStatusText("")
 
326
            # display results
 
327
            dlg = showhtml.ShowHTML(parent=self, content=strContent, 
 
328
                                    file_name=projects.INT_REPORT_FILE, 
 
329
                                    title="Report", 
 
330
                                    print_folder=projects.INTERNAL_FOLDER)
 
331
            dlg.ShowModal()
 
332
            dlg.Destroy()
 
333
        else:
 
334
            wx.MessageBox("Missing %s data" % missing_dim)
 
335
 
 
336
    #def OnRunEnterWindow(self, event):
 
337
    #    "Hover over RUN button"
 
338
    #    self.statusbar.SetStatusText("Export HTML table to file")
 
339
        
 
340
    # export script
 
341
    def OnButtonExport(self, event):
 
342
        """
 
343
        Export script if enough data to create table.
 
344
        """
 
345
        export_ok, missing_dim, has_rows, has_cols = self.TableConfigOK()
 
346
        if export_ok:
 
347
            self.ExportScript(has_rows, has_cols)
 
348
        else:
 
349
            wx.MessageBox("Missing %s data" % missing_dim) 
 
350
    
 
351
    def ExportScript(self, has_rows, has_cols):
 
352
        """
 
353
        Export script for table to file currently displayed.
 
354
        If the file doesn't exist, make one and add the preliminary code.
 
355
        If a file exists, but is empty, put the preliminary code in then
 
356
            the new exported script.
 
357
        If the file exists and is not empty, append the script on the end.
 
358
        """
 
359
        if self.fil_script in self.open_scripts:
 
360
            # see if empty or not
 
361
            f = file(self.fil_script, "r+")
 
362
            lines = f.readlines()
 
363
            empty_fil = False if lines else True            
 
364
            if empty_fil:
 
365
                self.InsertPrelimCode(fil=f)
 
366
            # insert exported script
 
367
            self.AppendExportedScript(f, has_rows, has_cols)
 
368
        else:
 
369
            # add file name to list, create file, insert preliminary code, 
 
370
            # and insert exported script.
 
371
            self.open_scripts.append(self.fil_script)
 
372
            f = file(self.fil_script, "w")
 
373
            self.InsertPrelimCode(fil=f)
 
374
            self.AppendExportedScript(f, has_rows, has_cols)
 
375
        f.close()
 
376
    
 
377
    def InsertPrelimCode(self, fil, fil_report=None):
 
378
        """
 
379
        Insert preliminary code at top of file.
 
380
        fil - open file handle ready for writing.
 
381
        NB files always start from scratch per make tables session.
 
382
        """         
 
383
        fil.write("#! /usr/bin/env python")
 
384
        fil.write("\n# -*- coding: utf-8 -*-\n")            
 
385
        fil.write("\nimport dimtables")
 
386
        fil.write("\nimport rawtables")
 
387
        fil.write("\nimport tabreports")
 
388
        fil.write("\nimport getdata\n")
 
389
        if not fil_report:
 
390
            fil_report = self.fil_report
 
391
        fil.write("\nfil = file(r\"%s\", \"w\")" % fil_report)
 
392
        fil.write("\nfil.write(tabreports.getHtmlHdr(\"Report(s)\", " + \
 
393
                  "fil_css=r\"%s\"))" % self.fil_css)
 
394
    
 
395
    def AppendExportedScript(self, fil, has_rows, has_cols):
 
396
        """
 
397
        Append exported script onto file.
 
398
        fil - open file handle ready for writing
 
399
        """
 
400
        datestamp = datetime.now().strftime("Script " + \
 
401
                                        "exported %d/%m/%Y at %I:%M %p")
 
402
        # Fresh connection for each in case it changes in between tables
 
403
        
 
404
        
 
405
        
 
406
        
 
407
        # TODO - shift into class
 
408
        if self.dbe == getdata.DBE_MYSQL:
 
409
            self.conn_dets["db"] = self.db
 
410
            
 
411
            
 
412
            
 
413
            
 
414
        conn_dets_str = pprint.pformat(self.conn_dets)
 
415
        fil.write("\nconn_dets = %s" % conn_dets_str)
 
416
        fil.write("\nconn, cur, dbs, tbls, flds, has_unique, idxs = \\" + \
 
417
            "\n    getdata.getDbDetsObj(" + \
 
418
            """dbe="%s", conn_dets=conn_dets, \n    db="%s", tbl="%s")""" % \
 
419
                (self.dbe, self.db, self.tbl_name) + \
 
420
            ".getDbDets()" )
 
421
        fil.write("\n\n#%s\n#%s\n" % ("-"*50, datestamp))
 
422
        fil.write(self.getScript(has_rows, has_cols))
 
423
        fil.write("\nconn.close()")
 
424
        
 
425
    def getScript(self, has_rows, has_cols):
 
426
        "Build script from inputs"
 
427
        script_lst = []
 
428
        # set up variables required for passing into main table instantiation
 
429
        if self.tab_type in [COL_MEASURES, ROW_SUMM]:
 
430
            script_lst.append("tree_rows = dimtables.DimNodeTree()")
 
431
            for child in util.getTreeCtrlChildren(tree=self.rowtree, 
 
432
                                                  parent=self.rowRoot):
 
433
                child_fld_name, _ = \
 
434
                    extractVarDets(self.rowtree.GetItemText(child))
 
435
                self.addToParent(script_lst=script_lst, tree=self.rowtree, 
 
436
                             parent=self.rowtree, 
 
437
                             parent_node_label="tree_rows",
 
438
                             child=child, child_fld_name=child_fld_name)
 
439
            script_lst.append("tree_cols = dimtables.DimNodeTree()")
 
440
            if has_cols:
 
441
                for child in util.getTreeCtrlChildren(tree=self.coltree, 
 
442
                                                      parent=self.colRoot):
 
443
                    child_fld_name, _ = \
 
444
                        extractVarDets(self.coltree.GetItemText(child))
 
445
                    self.addToParent(script_lst=script_lst, tree=self.coltree, 
 
446
                                 parent=self.coltree, 
 
447
                                 parent_node_label="tree_cols",
 
448
                                 child=child, child_fld_name=child_fld_name)
 
449
        elif self.tab_type == RAW_DISPLAY:
 
450
            col_names, col_labels = GetColDets(self.coltree, self.colRoot, 
 
451
                                               self.var_labels)
 
452
            script_lst.append("col_names = " + pprint.pformat(col_names))
 
453
            script_lst.append("col_labels = " + pprint.pformat(col_labels))
 
454
            script_lst.append("flds = " + pprint.pformat(self.flds))
 
455
            script_lst.append("var_labels = " + pprint.pformat(self.var_labels))
 
456
            script_lst.append("val_dics = " + pprint.pformat(self.val_dics))
 
457
        # process title dets
 
458
        titles = ["%s" % x for x \
 
459
                  in self.txtTitles.GetValue().split("\n")]
 
460
        subtitles = ["%s" % x for x \
 
461
                     in self.txtSubtitles.GetValue().split("\n")]
 
462
        # NB the following text is all going to be run
 
463
        if self.tab_type == COL_MEASURES:
 
464
            script_lst.append("tab_test = dimtables.GenTable(titles=" + \
 
465
                              str(titles) + ",\n    subtitles=" + \
 
466
                              str(subtitles) + \
 
467
                              ",\n    dbe=\"" + self.dbe + \
 
468
                              "\",\n    datasource=\"" + self.tbl_name + \
 
469
                              "\", cur=cur, tree_rows=tree_rows, " + \
 
470
                              "tree_cols=tree_cols)")
 
471
        elif self.tab_type == ROW_SUMM:
 
472
            script_lst.append("tab_test = dimtables.SummTable(titles=" + \
 
473
                              str(titles) + ",\n    subtitles=" + \
 
474
                              str(subtitles) + \
 
475
                              ",\n    dbe=\"" + self.dbe + \
 
476
                              "\",\n    datasource=\"" + self.tbl_name + \
 
477
                              "\", cur=cur, tree_rows=tree_rows, " + \
 
478
                              "tree_cols=tree_cols)")
 
479
        elif self.tab_type == RAW_DISPLAY:
 
480
            tot_rows = "True" if self.chkTotalsRow.IsChecked() else "False"
 
481
            first_label = "True" if self.chkFirstAsLabel.IsChecked() \
 
482
                else "False"
 
483
            script_lst.append("tab_test = rawtables.RawTable(titles=" + \
 
484
                str(titles) + ",\n    subtitles=" + \
 
485
                str(subtitles) + \
 
486
                ",\n    dbe=\"" + self.dbe + \
 
487
                "\",\n    datasource=\"%s\"" % self.tbl_name + ", cur=cur," + \
 
488
                " col_names=col_names, col_labels=col_labels, flds=flds, " + \
 
489
                "\n    var_labels=var_labels, val_dics=val_dics, " + \
 
490
                "add_total_row=%s, " % tot_rows + \
 
491
                "\nfirst_col_as_label=%s)" % first_label)
 
492
        if self.tab_type in [COL_MEASURES, ROW_SUMM]:
 
493
            script_lst.append("tab_test.prepTable()")
 
494
            script_lst.append("max_cells = 5000")
 
495
            script_lst.append("if tab_test.getCellNOk(max_cells=max_cells):")
 
496
            script_lst.append("    fil.write(tab_test.getHTML(page_break_after=False))")
 
497
            script_lst.append("else:")
 
498
            script_lst.append("    fil.write(\"Table not made.  Number \" + \\" + \
 
499
                              "\n        \"of cells exceeded limit \" + \\" + \
 
500
                              "\n        \"of %s\" % max_cells)")
 
501
        else:
 
502
            script_lst.append("fil.write(tab_test.getHTML(page_break_after=False))")
 
503
        return "\n".join(script_lst)
 
504
 
 
505
    def addToParent(self, script_lst, tree, parent, parent_node_label, 
 
506
                    child, child_fld_name):
 
507
        """
 
508
        Add script code for adding child nodes to parent nodes.
 
509
        tree - TreeListCtrl tree
 
510
        parent, child - TreeListCtrl items
 
511
        parent_node_label - for parent_node_label.addChild(...)
 
512
        child_fld_name - used to get variable label, and value labels
 
513
            from relevant dics; plus as the field name
 
514
        """
 
515
        # add child to parent
 
516
        if child == self.col_no_vars_item:
 
517
            fld_arg = ""
 
518
        else:
 
519
            fld_arg = "fld=\"%s\", " % child_fld_name
 
520
        #print self.var_labels #debug
 
521
        #print self.val_dics #debug
 
522
        var_label = self.var_labels.get(child_fld_name, 
 
523
                                        child_fld_name.title())
 
524
        labels_dic = self.val_dics.get(child_fld_name, {})
 
525
        child_node_label = "node_" + "_".join(child_fld_name.split(" "))
 
526
        item_conf = tree.GetItemPyData(child)
 
527
        measures_lst = item_conf.measures_lst
 
528
        measures = ", ".join([("dimtables." + \
 
529
                               dimtables.script_export_measures_dic[x]) for \
 
530
                               x in measures_lst])
 
531
        if measures:
 
532
            measures_arg = ", \n    measures=[%s]" % measures
 
533
        else:
 
534
            measures_arg = ""
 
535
        if item_conf.has_tot:
 
536
            tot_arg = ", \n    has_tot=True"
 
537
        else:
 
538
            tot_arg = ""
 
539
        sort_order_arg = ", \n    sort_order=\"%s\"" % \
 
540
            item_conf.sort_order
 
541
        numeric_arg = ", \n    bolnumeric=%s" % item_conf.bolnumeric
 
542
        script_lst.append(child_node_label + \
 
543
                          " = dimtables.DimNode(" + fld_arg + \
 
544
                          "\n    label=\"" + str(var_label) + \
 
545
                          "\", \n    labels=" + str(labels_dic) + \
 
546
                          measures_arg + tot_arg + sort_order_arg + \
 
547
                          numeric_arg + ")")
 
548
        script_lst.append("%s.addChild(%s)" % (parent_node_label, 
 
549
                                               child_node_label))
 
550
        # send child through for each grandchild
 
551
        for grandchild in util.getTreeCtrlChildren(tree=tree, 
 
552
                                                   parent=child):
 
553
            grandchild_fld_name, _ = \
 
554
                extractVarDets(tree.GetItemText(grandchild))
 
555
            self.addToParent(script_lst=script_lst, tree=tree, 
 
556
                             parent=child, 
 
557
                             parent_node_label=child_node_label,
 
558
                             child=grandchild, 
 
559
                             child_fld_name=grandchild_fld_name)
 
560
    
 
561
    #def OnExportEnterWindow(self, event):
 
562
    #    "Hover over EXPORT button"
 
563
    #    self.statusbar.SetStatusText("Export python code for making " + \
 
564
    #                                 "table to file")
 
565
        
 
566
    # clear button
 
567
    def ClearDims(self):
 
568
        "Clear dim trees"
 
569
        self.rowtree.DeleteChildren(self.rowRoot)
 
570
        self.coltree.DeleteChildren(self.colRoot)
 
571
        self.UpdateDemoDisplay()
 
572
 
 
573
    #def OnClearEnterWindow(self, event):
 
574
    #    "Hover over CLEAR button"
 
575
    #    self.statusbar.SetStatusText("Clear settings")
 
576
 
 
577
    def OnButtonClear(self, event):
 
578
        "Clear all settings"
 
579
        self.txtTitles.SetValue("")        
 
580
        self.txtSubtitles.SetValue("")
 
581
        self.radTabType.SetSelection(COL_MEASURES)
 
582
        self.tab_type = COL_MEASURES
 
583
        self.rowtree.DeleteChildren(self.rowRoot)
 
584
        self.coltree.DeleteChildren(self.colRoot)
 
585
        self.UpdateDemoDisplay()
 
586
    
 
587
    # help
 
588
    #def OnHelpEnterWindow(self, event):
 
589
    #    "Hover over HELP button"
 
590
    #    self.statusbar.SetStatusText("Get help")    
 
591
        
 
592
    # close
 
593
    #def OnCloseEnterWindow(self, event):
 
594
    #    "Hover over CLOSE button"
 
595
    #    self.statusbar.SetStatusText("Close application")
 
596
 
 
597
    def OnClose(self, event):
 
598
        "Close app"
 
599
        try:
 
600
            self.conn.close()
 
601
            # add end to each open script file and close.
 
602
            for fil_script in self.open_scripts:
 
603
                # add ending code to script
 
604
                f = file(fil_script, "a")
 
605
                AddClosingScriptCode(f)
 
606
                f.close()
 
607
        except Exception:
 
608
            pass
 
609
        finally:
 
610
            self.Destroy()
 
611
            
 
612
    # demo table display
 
613
    def UpdateDemoDisplay(self):
 
614
        "Update demo table display with random data"
 
615
        demo_tbl_html = self.demo_tab.getDemoHTMLIfOK()
 
616
        #print "\n" + demo_tbl_html + "\n" #debug
 
617
        self.html.ShowHTML(demo_tbl_html)
 
618
        
 
619
    # misc
 
620
    #def BlankStatusBar(self, event):
 
621
    #    """Blank the status bar"""
 
622
    #    self.statusbar.SetStatusText("")
 
623
 
 
624
    def TableConfigOK(self):
 
625
        """
 
626
        Is the table configuration sufficient to export as script or HTML?
 
627
        Summary only requires rows (can have both)
 
628
        Raw only requires cols (and cannot have rows)
 
629
        And gen requires both        
 
630
        """
 
631
        has_rows = util.getTreeCtrlChildren(tree=self.rowtree, 
 
632
                                    parent=self.rowRoot)
 
633
        has_cols = util.getTreeCtrlChildren(tree=self.coltree, 
 
634
                                         parent=self.colRoot)
 
635
        export_ok = False
 
636
        missing_dim = None
 
637
        if self.tab_type == ROW_SUMM:
 
638
            if has_rows:
 
639
                export_ok = True
 
640
            else:
 
641
                missing_dim = "row"
 
642
        elif self.tab_type == RAW_DISPLAY:
 
643
            if has_cols:
 
644
                export_ok = True
 
645
            else:
 
646
                missing_dim = "column"
 
647
        elif self.tab_type == COL_MEASURES:
 
648
            if has_rows and has_cols:
 
649
                export_ok = True
 
650
            else:
 
651
                missing_dim = "row and column"
 
652
        return (export_ok, missing_dim, has_rows, has_cols)
 
653
            
 
654
 
 
655
class ItemConfig(object):
 
656
    """
 
657
    Item config storage and retrieval.
 
658
    Has: measures, has_tot, sort order, bolnumeric
 
659
    """
 
660
    
 
661
    def __init__(self, measures_lst=None, has_tot=False, 
 
662
                 sort_order=dimtables.SORT_NONE, bolnumeric=False):
 
663
        if measures_lst:
 
664
            self.measures_lst = measures_lst
 
665
        else:
 
666
            self.measures_lst = []
 
667
        self.has_tot = has_tot
 
668
        self.sort_order = sort_order
 
669
        self.bolnumeric = bolnumeric
 
670
    
 
671
    def hasData(self):
 
672
        "Has the item got any extra config e.g. measures, a total?"
 
673
        return self.measures_lst or self.has_tot or \
 
674
            self.sort_order != dimtables.SORT_NONE
 
675
    
 
676
    def getSummary(self, verbose=False):
 
677
        "String summary of data"
 
678
        str_parts = []
 
679
        total_part = "Has TOTAL" if self.has_tot else None
 
680
        if total_part:
 
681
            str_parts.append(total_part)
 
682
        if self.sort_order == dimtables.SORT_NONE:
 
683
            sort_order_part = None
 
684
        elif self.sort_order == dimtables.SORT_LABEL:
 
685
            sort_order_part = "Sort by Label"
 
686
        elif self.sort_order == dimtables.SORT_FREQ_ASC:
 
687
            sort_order_part = "Sort by Freq (Asc)"
 
688
        elif self.sort_order == dimtables.SORT_FREQ_DESC:
 
689
            sort_order_part = "Sort by Freq (Desc)"            
 
690
        if sort_order_part:
 
691
            str_parts.append(sort_order_part)
 
692
        if verbose:
 
693
            if self.bolnumeric:
 
694
                str_parts.append("Numeric")
 
695
            else:
 
696
                str_parts.append("Not numeric")
 
697
        measures = ", ".join(self.measures_lst)
 
698
        measures_part = "Measures: %s" % measures if measures else None
 
699
        if measures_part:
 
700
            str_parts.append(measures_part)
 
701
        return "; ".join(str_parts)