GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
sqlbuilder.py
Go to the documentation of this file.
1 """!
2 @package dbmgr.sqlbuilder
3 
4 @brief GRASS SQL Builder
5 
6 Classes:
7  - sqlbuilder::SQLFrame
8 
9 Usage:
10 @code
11 python sqlbuilder.py vector_map
12 @endcode
13 
14 (C) 2007-2009, 2011 by the GRASS Development Team
15 
16 This program is free software under the GNU General Public License
17 (>=v2). Read the file COPYING that comes with GRASS for details.
18 
19 @author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
20 @author Martin Landa <landa.martin gmail.com>
21 @author Hamish Bowman <hamish_b yahoo com>
22 """
23 
24 import os
25 import sys
26 
27 if __name__ == "__main__":
28  sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
29 from core import globalvar
30 import wx
31 
32 from core.gcmd import RunCommand, GError
33 from dbmgr.vinfo import createDbInfoDesc, VectorDBInfo
34 
35 import grass.script as grass
36 
37 class SQLFrame(wx.Frame):
38  """!SQL Frame class"""
39  def __init__(self, parent, title, vectmap, id = wx.ID_ANY,
40  layer = 1, qtype = "select", evtHandler = None):
41 
42  wx.Frame.__init__(self, parent, id, title)
43 
44  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'),
45  wx.BITMAP_TYPE_ICO))
46 
47  self.parent = parent
48  self.evtHandler = evtHandler
49 
50  #
51  # variables
52  #
53  self.vectmap = vectmap # fullname
54  if not "@" in self.vectmap:
55  self.vectmap = grass.find_file(self.vectmap, element = 'vector')['fullname']
56  self.mapname, self.mapset = self.vectmap.split("@", 1)
57 
58  # db info
59  self.layer = layer
60  self.dbInfo = VectorDBInfo(self.vectmap)
61  self.tablename = self.dbInfo.GetTable(self.layer)
62  self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
63 
64  self.qtype = qtype # type of query: SELECT, UPDATE, DELETE, ...
65  self.colvalues = [] # array with unique values in selected column
66 
67  # set dialog title
68  self.SetTitle(_("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
69  { 'type' : self.qtype.upper(), 'map' : self.vectmap })
70 
71  self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
72 
73  # statusbar
74  self.statusbar = self.CreateStatusBar(number=1)
75  self.statusbar.SetStatusText(_("SQL statement not verified"), 0)
76 
77  self._doLayout()
78 
79  def _doLayout(self):
80  """!Do dialog layout"""
81 
82  pagesizer = wx.BoxSizer(wx.VERTICAL)
83 
84 
85  # dbInfo
86  databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
87  label = " %s " % _("Database connection"))
88  databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
89  databaseboxsizer.Add(item=createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
90  proportion=1,
91  flag=wx.EXPAND | wx.ALL,
92  border=3)
93 
94  #
95  # text areas
96  #
97  # sql box
98  sqlbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
99  label = " %s " % _("Query"))
100  sqlboxsizer = wx.StaticBoxSizer(sqlbox, wx.VERTICAL)
101 
102  self.text_sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
103  value = '', size = (-1, 50),
104  style=wx.TE_MULTILINE)
105  if self.qtype.lower() == "select":
106  self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
107  self.text_sql.SetInsertionPointEnd()
108  self.text_sql.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")
109  wx.CallAfter(self.text_sql.SetFocus)
110 
111  sqlboxsizer.Add(item = self.text_sql, flag = wx.EXPAND)
112 
113  #
114  # buttons
115  #
116  self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
117  self.btn_clear.SetToolTipString(_("Set SQL statement to default"))
118  self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
119  label = _("Verify"))
120  self.btn_verify.SetToolTipString(_("Verify SQL statement"))
121  self.btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
122  self.btn_apply.SetToolTipString(_("Apply SQL statement and close the dialog"))
123  self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
124  self.btn_close.SetToolTipString(_("Close the dialog"))
125 
126  self.btn_lv = { 'is' : ['=', ],
127  'isnot' : ['!=', ],
128  'like' : ['LIKE', ],
129  'gt' : ['>', ],
130  'ge' : ['>=', ],
131  'lt' : ['<', ],
132  'le' : ['<=', ],
133  'or' : ['OR', ],
134  'not' : ['NOT', ],
135  'and' : ['AND', ],
136  'brac' : ['()', ],
137  'prc' : ['%', ] }
138 
139  for key, value in self.btn_lv.iteritems():
140  btn = wx.Button(parent = self.panel, id = wx.ID_ANY,
141  label = value[0])
142  self.btn_lv[key].append(btn.GetId())
143 
144  buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
145  buttonsizer.Add(item = self.btn_clear)
146  buttonsizer.Add(item = self.btn_verify)
147  buttonsizer.Add(item = self.btn_apply)
148  buttonsizer.Add(item = self.btn_close)
149 
150  buttonsizer2 = wx.GridBagSizer(5, 5)
151  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['is'][1]), pos = (0,0))
152  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['isnot'][1]), pos = (1,0))
153  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['like'][1]), pos = (2, 0))
154 
155  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['gt'][1]), pos = (0, 1))
156  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['ge'][1]), pos = (1, 1))
157  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['or'][1]), pos = (2, 1))
158 
159  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['lt'][1]), pos = (0, 2))
160  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['le'][1]), pos = (1, 2))
161  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['not'][1]), pos = (2, 2))
162 
163  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['brac'][1]), pos = (0, 3))
164  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['prc'][1]), pos = (1, 3))
165  buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['and'][1]), pos = (2, 3))
166 
167  #
168  # list boxes (columns, values)
169  #
170  hsizer = wx.BoxSizer(wx.HORIZONTAL)
171 
172  columnsbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
173  label = " %s " % _("Columns"))
174  columnsizer = wx.StaticBoxSizer(columnsbox, wx.VERTICAL)
175  self.list_columns = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
176  choices = self.dbInfo.GetColumns(self.tablename),
177  style = wx.LB_MULTIPLE)
178  columnsizer.Add(item = self.list_columns, proportion = 1,
179  flag = wx.EXPAND)
180 
181  radiosizer = wx.BoxSizer(wx.HORIZONTAL)
182  self.radio_cv = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
183  label = " %s " % _("Add on double-click"),
184  choices = [_("columns"), _("values")])
185  self.radio_cv.SetSelection(1) # default 'values'
186  radiosizer.Add(item = self.radio_cv, proportion = 1,
187  flag = wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND, border = 5)
188 
189  columnsizer.Add(item = radiosizer, proportion = 0,
190  flag = wx.TOP | wx.EXPAND, border = 5)
191  # self.list_columns.SetMinSize((-1,130))
192  # self.list_values.SetMinSize((-1,100))
193 
194  valuesbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
195  label = " %s " % _("Values"))
196  valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
197  self.list_values = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
198  choices = self.colvalues,
199  style = wx.LB_MULTIPLE)
200  valuesizer.Add(item = self.list_values, proportion = 1,
201  flag = wx.EXPAND)
202 
203  self.btn_unique = wx.Button(parent = self.panel, id = wx.ID_ANY,
204  label = _("Get all values"))
205  self.btn_unique.Enable(False)
206  self.btn_uniquesample = wx.Button(parent = self.panel, id = wx.ID_ANY,
207  label = _("Get sample"))
208  self.btn_uniquesample.Enable(False)
209 
210  buttonsizer3 = wx.BoxSizer(wx.HORIZONTAL)
211  buttonsizer3.Add(item = self.btn_uniquesample, proportion = 0,
212  flag = wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border = 5)
213  buttonsizer3.Add(item = self.btn_unique, proportion = 0,
214  flag = wx.ALIGN_CENTER_HORIZONTAL)
215 
216  valuesizer.Add(item = buttonsizer3, proportion = 0,
217  flag = wx.TOP, border = 5)
218 
219  # hsizer1.Add(wx.StaticText(self.panel,-1, "Unique values: "), border=0, proportion=1)
220 
221  hsizer.Add(item = columnsizer, proportion = 1,
222  flag = wx.EXPAND)
223  hsizer.Add(item = valuesizer, proportion = 1,
224  flag = wx.EXPAND)
225 
226  self.close_onapply = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
227  label = _("Close dialog on apply"))
228  self.close_onapply.SetValue(True)
229 
230  pagesizer.Add(item = databaseboxsizer,
231  flag = wx.ALL | wx.EXPAND, border = 5)
232  pagesizer.Add(item = hsizer, proportion = 1,
233  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
234  # pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
235  # pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
236  pagesizer.Add(item = buttonsizer2, proportion = 0,
237  flag = wx.ALIGN_CENTER_HORIZONTAL)
238  pagesizer.Add(item = sqlboxsizer, proportion = 0,
239  flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
240  pagesizer.Add(item = buttonsizer, proportion = 0,
241  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
242  pagesizer.Add(item = self.close_onapply, proportion = 0,
243  flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
244 
245  #
246  # bindings
247  #
248  self.btn_unique.Bind(wx.EVT_BUTTON, self.OnUniqueValues)
249  self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.OnSampleValues)
250 
251  for key, value in self.btn_lv.iteritems():
252  self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)
253 
254  self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
255  self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
256  self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
257  self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
258 
259  self.list_columns.Bind(wx.EVT_LISTBOX, self.OnAddColumn)
260  self.list_values.Bind(wx.EVT_LISTBOX, self.OnAddValue)
261 
262  self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
263 
264  self.panel.SetAutoLayout(True)
265  self.panel.SetSizer(pagesizer)
266  pagesizer.Fit(self.panel)
267 
268  self.Layout()
269  self.SetMinSize((660, 525))
270  self.SetClientSize(self.panel.GetSize())
271  self.CenterOnParent()
272 
273  def OnUniqueValues(self, event, justsample = False):
274  """!Get unique values"""
275  vals = []
276  try:
277  idx = self.list_columns.GetSelections()[0]
278  column = self.list_columns.GetString(idx)
279  except:
280  self.list_values.Clear()
281  return
282 
283  self.list_values.Clear()
284 
285  data = grass.db_select(sql = "SELECT %s FROM %s" % (column, self.tablename),
286  database = self.database,
287  driver = self.driver)
288  if not data:
289  return
290 
291  desc = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]
292 
293  i = 0
294  for item in sorted(set(map(lambda x: desc['ctype'](x[0]), data))):
295  if justsample and i > 255:
296  break
297 
298  if desc['type'] != 'character':
299  item = str(item)
300  self.list_values.Append(item)
301  i += 1
302 
303  def OnSampleValues(self, event):
304  """!Get sample values"""
305  self.OnUniqueValues(None, True)
306 
307  def OnAddColumn(self, event):
308  """!Add column name to the query"""
309  idx = self.list_columns.GetSelections()
310  for i in idx:
311  column = self.list_columns.GetString(i)
312  self._add(element = 'column', value = column)
313 
314  if not self.btn_uniquesample.IsEnabled():
315  self.btn_uniquesample.Enable(True)
316  self.btn_unique.Enable(True)
317 
318  def OnAddValue(self, event):
319  """!Add value"""
320  selection = self.list_values.GetSelections()
321  if not selection:
322  event.Skip()
323  return
324 
325  idx = selection[0]
326  value = self.list_values.GetString(idx)
327  idx = self.list_columns.GetSelections()[0]
328  column = self.list_columns.GetString(idx)
329 
330  ctype = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]['type']
331 
332  if ctype == 'character':
333  value = "'%s'" % value
334 
335  self._add(element = 'value', value = value)
336 
337  def OnAddMark(self, event):
338  """!Add mark"""
339  mark = None
340  for key, value in self.btn_lv.iteritems():
341  if event.GetId() == value[1]:
342  mark = value[0]
343  break
344 
345  self._add(element = 'mark', value = mark)
346 
347  def _add(self, element, value):
348  """!Add element to the query
349 
350  @param element element to add (column, value)
351  """
352  sqlstr = self.text_sql.GetValue()
353  newsqlstr = ''
354  if element == 'column':
355  if self.radio_cv.GetSelection() == 0: # -> column
356  idx1 = len('select')
357  idx2 = sqlstr.lower().find('from')
358  colstr = sqlstr[idx1:idx2].strip()
359  if colstr == '*':
360  cols = []
361  else:
362  cols = colstr.split(',')
363  if value in cols:
364  cols.remove(value)
365  else:
366  cols.append(value)
367 
368  if len(cols) < 1:
369  cols = ['*',]
370 
371  newsqlstr = 'SELECT ' + ','.join(cols) + ' ' + sqlstr[idx2:]
372  else: # -> where
373  newsqlstr = sqlstr
374  if sqlstr.lower().find('where') < 0:
375  newsqlstr += ' WHERE'
376 
377  newsqlstr += ' ' + value
378 
379  elif element == 'value':
380  newsqlstr = sqlstr + ' ' + value
381  elif element == 'mark':
382  newsqlstr = sqlstr + ' ' + value
383 
384  if newsqlstr:
385  self.text_sql.SetValue(newsqlstr)
386 
387  def GetSQLStatement(self):
388  """!Return SQL statement"""
389  return self.text_sql.GetValue().strip().replace("\n"," ")
390 
391  def CloseOnApply(self):
392  """!Return True if the dialog will be close on apply"""
393  return self.close_onapply.IsChecked()
394 
395  def OnText(self, event):
396  """Query string changed"""
397  if len(self.text_sql.GetValue()) > 0:
398  self.btn_verify.Enable(True)
399  else:
400  self.btn_verify.Enable(False)
401 
402  def OnApply(self, event):
403  """Apply button pressed"""
404  if self.evtHandler:
405  self.evtHandler(event = 'apply')
406 
407  if self.close_onapply.IsChecked():
408  self.Destroy()
409 
410  event.Skip()
411 
412  def OnVerify(self, event):
413  """!Verify button pressed"""
414  ret, msg = RunCommand('db.select',
415  getErrorMsg = True,
416  table = self.tablename,
417  sql = self.text_sql.GetValue(),
418  flags = 't',
419  driver = self.driver,
420  database = self.database)
421 
422  if ret != 0 and msg:
423  self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
424  GError(parent = self,
425  message = _("SQL statement is not valid.\n\n%s") % msg)
426  else:
427  self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
428 
429  def OnClear(self, event):
430  """!Clear button pressed"""
431  if self.qtype.lower() == "select":
432  self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
433  else:
434  self.text_sql.SetValue("")
435 
436  def OnClose(self, event):
437  """!Close button pressed"""
438  if self.evtHandler:
439  self.evtHandler(event = 'close')
440 
441  self.Destroy()
442 
443  event.Skip()
444 
445 if __name__ == "__main__":
446  if len(sys.argv) != 2:
447  print >>sys.stderr, __doc__
448  sys.exit()
449 
450  import gettext
451  gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
452 
453  app = wx.App(0)
454  sqlb = SQLFrame(parent = None, title = _('SQL Builder'), vectmap = sys.argv[1])
455  sqlb.Show()
456 
457  app.MainLoop()
def OnAddValue
Add value.
Definition: sqlbuilder.py:318
wxGUI command interface
def createDbInfoDesc
Create database connection information content.
Definition: vinfo.py:41
def OnUniqueValues
Get unique values.
Definition: sqlbuilder.py:273
def OnVerify
Verify button pressed.
Definition: sqlbuilder.py:412
def OnClose
Close button pressed.
Definition: sqlbuilder.py:436
def _doLayout
Do dialog layout.
Definition: sqlbuilder.py:79
def OnAddMark
Add mark.
Definition: sqlbuilder.py:337
SQL Frame class.
Definition: sqlbuilder.py:37
def GetSQLStatement
Return SQL statement.
Definition: sqlbuilder.py:387
def OnClear
Clear button pressed.
Definition: sqlbuilder.py:429
def OnAddColumn
Add column name to the query.
Definition: sqlbuilder.py:307
def _add
Add element to the query.
Definition: sqlbuilder.py:347
def CloseOnApply
Return True if the dialog will be close on apply.
Definition: sqlbuilder.py:391
string set
def RunCommand
Run GRASS command.
Definition: gcmd.py:625
def OnSampleValues
Get sample values.
Definition: sqlbuilder.py:303