sqlbuilder.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. """!
  2. @package sqlbuilder.py
  3. @brief GRASS SQL Builder
  4. Classes:
  5. - SQLFrame
  6. @todo Various updates are required...
  7. Usage:
  8. @code
  9. python sqlbuilder.py vector_map
  10. @endcode
  11. (C) 2007-2009 by the GRASS Development Team
  12. This program is free software under the GNU General Public
  13. License (>=v2). Read the file COPYING that comes with GRASS
  14. for details.
  15. @author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
  16. @author Martin Landa <landa.martin gmail.com>,
  17. @author Hamish Bowman <hamish_b yahoo com>
  18. """
  19. import os
  20. import sys
  21. import time
  22. ### i18N
  23. import gettext
  24. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
  25. import globalvar
  26. if not os.getenv("GRASS_WXBUNDLED"):
  27. globalvar.CheckForWx()
  28. import wx
  29. import grass.script as grass
  30. grass.set_fatal_exit(False)
  31. import gcmd
  32. import dbm_base
  33. class SQLFrame(wx.Frame):
  34. """!SQL Frame class"""
  35. def __init__(self, parent, title, vectmap, id = wx.ID_ANY,
  36. layer = 1, qtype = "select"):
  37. wx.Frame.__init__(self, parent, id, title)
  38. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'),
  39. wx.BITMAP_TYPE_ICO))
  40. self.parent = parent
  41. #
  42. # variables
  43. #
  44. self.vectmap = vectmap # fullname
  45. if not "@" in self.vectmap:
  46. self.vectmap = self.vectmap + "@" + grass.gisenv()['MAPSET']
  47. self.mapname, self.mapset = self.vectmap.split("@")
  48. # db info
  49. self.layer = layer
  50. self.dbInfo = dbm_base.VectorDBInfo(self.vectmap)
  51. self.tablename = self.dbInfo.GetTable(self.layer)
  52. self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
  53. self.qtype = qtype # type of the uqery: SELECT, UPDATE, DELETE, ...
  54. self.colvalues = [] # array with unique values in selected column
  55. # set dialog title
  56. self.SetTitle(_("GRASS SQL Builder (%s): vector map <%s>") % \
  57. (qtype.upper(), self.vectmap))
  58. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  59. #
  60. # buttons
  61. #
  62. self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
  63. self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
  64. label = _("Verify"))
  65. # self.btn_help = wx.Button(self.panel, -1, "Help")
  66. # self.btn_load = wx.Button(self.panel, -1, "Load")
  67. # self.btn_save = wx.Button(self.panel, -1, "Save")
  68. self.btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
  69. self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  70. self.btn_unique = wx.Button(parent = self.panel, id = wx.ID_ANY,
  71. label = _("Get all values"))
  72. self.btn_unique.Enable(False)
  73. self.btn_uniquesample = wx.Button(parent = self.panel, id = wx.ID_ANY,
  74. label = _("Get sample"))
  75. self.btn_uniquesample.Enable(False)
  76. self.btn_is = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "=")
  77. self.btn_isnot = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "!=")
  78. self.btn_like = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "LIKE")
  79. self.btn_gt = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">")
  80. self.btn_gtis = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">=")
  81. self.btn_lt = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<")
  82. self.btn_ltis = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<=")
  83. self.btn_or = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "OR")
  84. self.btn_not = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "NOT")
  85. self.btn_and = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "AND")
  86. self.btn_brackets = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "()")
  87. self.btn_prc = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "%")
  88. #
  89. # Text labels
  90. #
  91. # self.label_headding = wx.StaticText(self.panel, -1, '')
  92. #
  93. # Textareas
  94. #
  95. self.text_sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  96. value = '', size = (-1, 75),
  97. style=wx.TE_MULTILINE)
  98. if self.qtype.lower() == "select":
  99. self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
  100. #
  101. # List Boxes
  102. #
  103. self.list_columns = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
  104. choices = self.dbInfo.GetColumns(self.tablename),
  105. style = wx.LB_MULTIPLE)
  106. self.list_values = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
  107. choices = self.colvalues,
  108. style = wx.LB_MULTIPLE)
  109. #
  110. # Bindings
  111. #
  112. self.btn_unique.Bind(wx.EVT_BUTTON, self.GetUniqueValues)
  113. self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.GetSampleValues)
  114. self.btn_is.Bind(wx.EVT_BUTTON, self.AddMark)
  115. self.btn_isnot.Bind(wx.EVT_BUTTON, self.AddMark)
  116. self.btn_like.Bind(wx.EVT_BUTTON, self.AddMark)
  117. self.btn_gt.Bind(wx.EVT_BUTTON, self.AddMark)
  118. self.btn_gtis.Bind(wx.EVT_BUTTON, self.AddMark)
  119. self.btn_or.Bind(wx.EVT_BUTTON, self.AddMark)
  120. self.btn_lt.Bind(wx.EVT_BUTTON, self.AddMark)
  121. self.btn_ltis.Bind(wx.EVT_BUTTON, self.AddMark)
  122. self.btn_not.Bind(wx.EVT_BUTTON, self.AddMark)
  123. self.btn_brackets.Bind(wx.EVT_BUTTON, self.AddMark)
  124. self.btn_prc.Bind(wx.EVT_BUTTON, self.AddMark)
  125. self.btn_and.Bind(wx.EVT_BUTTON, self.AddMark)
  126. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  127. self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
  128. self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
  129. self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
  130. self.list_columns.Bind(wx.EVT_LISTBOX, self.AddColumnName)
  131. self.list_values.Bind(wx.EVT_LISTBOX, self.AddValue)
  132. self.text_sql.Bind(wx.EVT_TEXT, self.OnText)
  133. self.__doLayout()
  134. def __doLayout(self):
  135. """!Do dialog layout"""
  136. # dbInfo
  137. databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  138. label = " %s " % _("Database connection"))
  139. databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
  140. databaseboxsizer.Add(item=dbm_base.createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
  141. proportion=1,
  142. flag=wx.EXPAND | wx.ALL,
  143. border=3)
  144. # sql box
  145. sqlbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  146. label = " %s " % _("Query"))
  147. sqlboxsizer = wx.StaticBoxSizer(sqlbox, wx.VERTICAL)
  148. sqlboxsizer.Add(item = self.text_sql, flag = wx.EXPAND)
  149. pagesizer = wx.BoxSizer(wx.VERTICAL)
  150. # buttons
  151. buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
  152. buttonsizer.Add(item = self.btn_clear)
  153. buttonsizer.Add(item = self.btn_verify)
  154. # buttonsizer1.Add(self.btn_help, (0,2))
  155. # buttonsizer1.Add(self.btn_load, (0,2))
  156. # buttonsizer1.Add(self.btn_save, (0,3))
  157. buttonsizer.Add(item = self.btn_apply)
  158. buttonsizer.Add(item = self.btn_close)
  159. buttonsizer2 = wx.GridBagSizer(5, 5)
  160. buttonsizer2.Add(item = self.btn_is, pos = (0,0))
  161. buttonsizer2.Add(item = self.btn_isnot, pos = (1,0))
  162. buttonsizer2.Add(item = self.btn_like, pos = (2, 0))
  163. buttonsizer2.Add(item = self.btn_gt, pos = (0, 1))
  164. buttonsizer2.Add(item = self.btn_gtis, pos = (1, 1))
  165. buttonsizer2.Add(item = self.btn_or, pos = (2, 1))
  166. buttonsizer2.Add(item = self.btn_lt, pos = (0, 2))
  167. buttonsizer2.Add(item = self.btn_ltis, pos = (1, 2))
  168. buttonsizer2.Add(item = self.btn_not, pos = (2, 2))
  169. buttonsizer2.Add(item = self.btn_brackets, pos = (0, 3))
  170. buttonsizer2.Add(item = self.btn_prc, pos = (1, 3))
  171. buttonsizer2.Add(item = self.btn_and, pos = (2, 3))
  172. buttonsizer4 = wx.BoxSizer(wx.HORIZONTAL)
  173. buttonsizer4.Add(item = self.btn_uniquesample, proportion = 0,
  174. flag = wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border = 5)
  175. buttonsizer4.Add(item = self.btn_unique, proportion = 0,
  176. flag = wx.ALIGN_CENTER_HORIZONTAL)
  177. hsizer = wx.BoxSizer(wx.HORIZONTAL)
  178. columnsbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  179. label = " %s " % _("Columns"))
  180. valuesbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  181. label = " %s " % _("Values"))
  182. # hsizer1.Add(wx.StaticText(self.panel,-1, "Unique values: "), border=0, proportion=1)
  183. columnsizer = wx.StaticBoxSizer(columnsbox, wx.VERTICAL)
  184. valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
  185. columnsizer.Add(item = self.list_columns, proportion = 1,
  186. flag = wx.EXPAND)
  187. valuesizer.Add(item = self.list_values, proportion = 1,
  188. flag = wx.EXPAND)
  189. # self.list_columns.SetMinSize((-1,130))
  190. # self.list_values.SetMinSize((-1,100))
  191. valuesizer.Add(item = buttonsizer4, proportion = 0,
  192. flag = wx.TOP, border = 5)
  193. hsizer.Add(item = columnsizer, proportion = 1,
  194. flag = wx.EXPAND)
  195. hsizer.Add(item = valuesizer, proportion = 1,
  196. flag = wx.EXPAND)
  197. pagesizer.Add(item = databaseboxsizer,
  198. flag = wx.ALL | wx.EXPAND, border = 5)
  199. pagesizer.Add(item = hsizer, proportion = 1,
  200. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
  201. # pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
  202. # pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
  203. pagesizer.Add(item = buttonsizer2, proportion = 0,
  204. flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM, border = 5)
  205. pagesizer.Add(item = sqlboxsizer, proportion = 0,
  206. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  207. pagesizer.Add(item = buttonsizer, proportion = 0,
  208. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  209. self.panel.SetAutoLayout(True)
  210. self.panel.SetSizer(pagesizer)
  211. pagesizer.Fit(self.panel)
  212. self.Layout()
  213. self.SetMinSize((660, 480))
  214. def GetUniqueValues(self, event, justsample = False):
  215. """!Get unique values"""
  216. vals = []
  217. try:
  218. idx = self.list_columns.GetSelections()[0]
  219. column = self.list_columns.GetString(idx)
  220. except:
  221. self.list_values.Clear()
  222. return
  223. self.list_values.Clear()
  224. i = 0
  225. querystring = "SELECT %s FROM %s" % (column, self.tablename)
  226. data = grass.db_select(table = self.tablename,
  227. sql = querystring,
  228. database = self.database,
  229. driver = self.driver)
  230. for line in data:
  231. if justsample and i < 256 or \
  232. not justsample:
  233. self.list_values.Append(line.strip())
  234. else:
  235. break
  236. i += 1
  237. def GetSampleValues(self, event):
  238. """!Get sample values"""
  239. self.GetUniqueValues(None, True)
  240. def AddColumnName(self, event):
  241. """!Add column name to the query"""
  242. idx = self.list_columns.GetSelections()
  243. for i in idx:
  244. column = self.list_columns.GetString(i)
  245. self.__Add(element = 'column', value = column)
  246. if not self.btn_uniquesample.IsEnabled():
  247. self.btn_uniquesample.Enable()
  248. self.btn_unique.Enable()
  249. def AddValue(self,event):
  250. """!Add value"""
  251. idx = self.list_values.GetSelections()[0]
  252. value = self.list_values.GetString(idx)
  253. idx = self.list_columns.GetSelections()[0]
  254. column = self.list_columns.GetString(idx)
  255. if self.columns[column]['type'].lower().find("chara") > -1:
  256. value = "'%s'" % value
  257. self.__Add(value)
  258. def AddMark(self,event):
  259. """!Add mark"""
  260. if event.GetId() == self.btn_is.GetId():
  261. mark = "="
  262. elif event.GetId() == self.btn_isnot.GetId():
  263. mark = "!="
  264. elif event.GetId() == self.btn_like.GetId():
  265. mark = "LIKE"
  266. elif event.GetId() == self.btn_gt.GetId():
  267. mark = ">"
  268. elif event.GetId() == self.btn_gtis.GetId():
  269. mark = ">="
  270. elif event.GetId() == self.btn_lt.GetId():
  271. mark = "<"
  272. elif event.GetId() == self.btn_ltis.GetId():
  273. mark = "<="
  274. elif event.GetId() == self.btn_or.GetId():
  275. mark = "OR"
  276. elif event.GetId() == self.btn_not.GetId():
  277. mark = "NOT"
  278. elif event.GetId() == self.btn_and.GetId():
  279. mark = "AND"
  280. elif event.GetId() == self.btn_brackets.GetId():
  281. mark = "()"
  282. elif event.GetId() == self.btn_prc.GetId():
  283. mark = "%"
  284. self.__Add(mark)
  285. def __Add(self, element, value):
  286. """!Add element to the query
  287. @param what what to add
  288. """
  289. sqlstr = self.text_sql.GetValue()
  290. newsqlstr = ''
  291. if element == 'column':
  292. idx1 = len('select')
  293. idx2 = sqlstr.lower().find('from')
  294. colstr = sqlstr[idx1:idx2].strip()
  295. if colstr == '*':
  296. cols = []
  297. else:
  298. cols = colstr.split(',')
  299. if value in cols:
  300. cols.remove(value)
  301. else:
  302. cols.append(value)
  303. if len(cols) < 1:
  304. cols = ['*',]
  305. newsqlstr = 'SELECT ' + ','.join(cols) + ' ' + sqlstr[idx2:]
  306. if newsqlstr:
  307. self.text_sql.SetValue(newsqlstr)
  308. def OnText(self, event):
  309. """Query string changed"""
  310. if len(self.text_sql.GetValue()) > 0:
  311. self.btn_verify.Enable(True)
  312. else:
  313. self.btn_verify.Enable(False)
  314. def OnApply(self, event):
  315. """Apply button pressed"""
  316. if self.parent:
  317. try:
  318. self.parent.text_query.SetValue= self.text_sql.GetValue().strip().replace("\n"," ")
  319. except:
  320. pass
  321. def OnVerify(self, event):
  322. """!Verify button pressed"""
  323. ret, msg = gcmd.RunCommand('db.select',
  324. getErrorMsg = True,
  325. table = self.tablename,
  326. sql = self.text_sql.GetValue(),
  327. flags = 't',
  328. driver = self.driver,
  329. database = self.database)
  330. if ret != 0 and msg:
  331. wx.MessageBox(parent=self,
  332. message=_("SQL statement is not valid.\n\n%s") % msg,
  333. caption=_("Warning"), style=wx.OK | wx.ICON_WARNING | wx.CENTRE)
  334. def OnClear(self, event):
  335. """!Clear button pressed"""
  336. if self.qtype.lower() == "select":
  337. self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
  338. else:
  339. self.text_sql.SetValue("")
  340. def OnClose(self, event):
  341. """!Close button pressed"""
  342. self.Destroy()
  343. if __name__ == "__main__":
  344. if len(sys.argv) != 2:
  345. print >>sys.stderr, __doc__
  346. sys.exit()
  347. app = wx.App(0)
  348. sqlb = SQLFrame(parent = None, title = _('SQL Builder'), vectmap = sys.argv[1])
  349. sqlb.Show()
  350. app.MainLoop()