sqlbuilder.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. """
  2. MODULE: sqlbuilder.py
  3. CLASSES:
  4. * SQLFrame
  5. PURPOSE: GRASS SQL Builder
  6. Usage:
  7. sqlbuilder.py table_name
  8. AUTHOR(S): GRASS Development Team
  9. Original author: Jachym Cepicky <jachym.cepicky gmail.com>
  10. Various updates: Martin Landa <landa.martin gmail.com>,
  11. Hamish Bowman <hamish_b yahoo com>
  12. COPYRIGHT: (C) 2007-2009 by the GRASS Development Team
  13. This program is free software under the GNU General Public
  14. License (>=v2). Read the file COPYING that comes with GRASS
  15. for details.
  16. """
  17. import wx
  18. import os,sys
  19. import time
  20. from grass.script import core as grass
  21. import gcmd
  22. import globalvar
  23. imagePath = os.path.join(globalvar.ETCWXDIR)
  24. sys.path.append(imagePath)
  25. import images
  26. imagepath = images.__path__[0]
  27. sys.path.append(imagepath)
  28. class SQLFrame(wx.Frame):
  29. """!SQL Frame class"""
  30. def __init__(self, parent, id, title, vectmap, qtype="select"):
  31. wx.Frame.__init__(self, parent, id, title)
  32. self.SetTitle(_("GRASS SQL Builder: %s") % (qtype.upper()))
  33. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'), wx.BITMAP_TYPE_ICO))
  34. #
  35. # variables
  36. #
  37. self.vectmap = vectmap
  38. if not "@" in self.vectmap:
  39. self.vectmap = self.vectmap + "@" + grass.gisenv()['MAPSET']
  40. self.mapname, self.mapset = self.vectmap.split("@")
  41. #FIXME: pass layer number to v.db.connect ???
  42. ret = gcmd.RunCommand('v.db.connect',
  43. quiet = True,
  44. read = True,
  45. flags = 'g',
  46. fs = '|',
  47. map = self.vectmap)
  48. for line in ret.splitlines():
  49. self.layer,self.tablename, self.column, self.database, self.driver =\
  50. line.strip().split("|")
  51. self.qtype = qtype # type of the uqery: SELECT, UPDATE, DELETE, ...
  52. self.column_names = [] # array with column names
  53. self.columns = {} # array with colum properties
  54. self.colvalues = [] # array with uniqe values in selected column
  55. self.heading = ""
  56. self.parent = parent
  57. if self.qtype.lower()=="select":
  58. self.heading = "SELECT * FROM %s WHERE" % self.tablename
  59. # Init
  60. self.GetColumns()
  61. #
  62. # Buttons
  63. #
  64. self.btn_clear = wx.Button(self, -1, "Clear")
  65. self.btn_verify = wx.Button(self, -1, "Verify")
  66. #self.btn_help = wx.Button(self, -1, "Help")
  67. # self.btn_load = wx.Button(self, -1, "Load")
  68. # self.btn_save = wx.Button(self, -1, "Save")
  69. self.btn_apply = wx.Button(self, -1, "Apply")
  70. self.btn_close = wx.Button(self, -1, "Close")
  71. self.btn_uniqe = wx.Button(self, -1, "Get all values")
  72. self.btn_uniqesample = wx.Button(self, -1, "Get sample")
  73. self.btn_is = wx.Button(self, -1, "=")
  74. self.btn_isnot = wx.Button(self, -1, "!=")
  75. self.btn_like = wx.Button(self, -1, "LIKE")
  76. self.btn_gt = wx.Button(self, -1, ">")
  77. self.btn_gtis = wx.Button(self, -1, ">=")
  78. self.btn_lt = wx.Button(self, -1, "<")
  79. self.btn_ltis = wx.Button(self, -1, "<=")
  80. self.btn_or = wx.Button(self, -1, "OR")
  81. self.btn_not = wx.Button(self, -1, "NOT")
  82. self.btn_and = wx.Button(self, -1, "AND")
  83. self.btn_brackets = wx.Button(self, -1, "()")
  84. self.btn_prc = wx.Button(self, -1, "%")
  85. #
  86. # Text labels
  87. #
  88. #self.label_headding = wx.StaticText(self, -1, '')
  89. #
  90. # Textareas
  91. #
  92. self.text_sql = wx.TextCtrl(self, -1, '', size=(-1,75),style=wx.TE_MULTILINE)
  93. #
  94. # List Boxes
  95. #
  96. self.list_columns = wx.ListBox(self, -1, wx.DefaultPosition, (-1, -1), self.columns.keys(), wx.LB_MULTIPLE|wx.LB_SORT)
  97. self.list_values = wx.ListBox(self, -1, wx.DefaultPosition, (-1, -1), self.colvalues, wx.LB_MULTIPLE|wx.LB_SORT)
  98. #
  99. # Bindings
  100. #
  101. self.btn_uniqe.Bind(wx.EVT_BUTTON, self.GetUniqueValues)
  102. self.btn_uniqesample.Bind(wx.EVT_BUTTON, self.GetSampleValues)
  103. self.btn_is.Bind(wx.EVT_BUTTON, self.AddMark)
  104. self.btn_isnot.Bind(wx.EVT_BUTTON, self.AddMark)
  105. self.btn_like.Bind(wx.EVT_BUTTON, self.AddMark)
  106. self.btn_gt.Bind(wx.EVT_BUTTON, self.AddMark)
  107. self.btn_gtis.Bind(wx.EVT_BUTTON, self.AddMark)
  108. self.btn_or.Bind(wx.EVT_BUTTON, self.AddMark)
  109. self.btn_lt.Bind(wx.EVT_BUTTON, self.AddMark)
  110. self.btn_ltis.Bind(wx.EVT_BUTTON, self.AddMark)
  111. self.btn_not.Bind(wx.EVT_BUTTON, self.AddMark)
  112. self.btn_brackets.Bind(wx.EVT_BUTTON, self.AddMark)
  113. self.btn_prc.Bind(wx.EVT_BUTTON, self.AddMark)
  114. self.btn_and.Bind(wx.EVT_BUTTON, self.AddMark)
  115. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  116. self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
  117. self.btn_verify.Bind(wx.EVT_BUTTON, self.OnVerify)
  118. self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
  119. self.list_columns.Bind(wx.EVT_LISTBOX, self.AddColumnName)
  120. self.list_values.Bind(wx.EVT_LISTBOX, self.AddValue)
  121. self.__doLayout()
  122. def __doLayout(self):
  123. databasebox = wx.StaticBox(self, -1, "Database connection")
  124. databaseboxsizer = wx.StaticBoxSizer(databasebox,wx.VERTICAL)
  125. dbstr = "Database: %s\n" % (self.database)
  126. dbstr += "Driver: %s\n" % (self.driver)
  127. dbstr += "Table: %s" % (self.tablename)
  128. databaseboxsizer.Add(wx.StaticText(self,-1,dbstr), flag=wx.EXPAND)
  129. sqlbox = wx.StaticBox(self, -1, "%s" % self.heading)
  130. sqlboxsizer = wx.StaticBoxSizer(sqlbox,wx.VERTICAL)
  131. sqlboxsizer.Add(self.text_sql, flag=wx.EXPAND)
  132. pagesizer = wx.BoxSizer(wx.VERTICAL)
  133. buttonsizer1 = wx.GridBagSizer(2,2)
  134. buttonsizer1.Add(self.btn_clear, (0,0))
  135. buttonsizer1.Add(self.btn_verify, (0,1))
  136. #buttonsizer1.Add(self.btn_help, (0,2))
  137. #buttonsizer1.Add(self.btn_load, (0,2))
  138. # buttonsizer1.Add(self.btn_save, (0,3))
  139. buttonsizer1.Add(self.btn_apply, (0,2))
  140. buttonsizer2 = wx.GridBagSizer(2, 2)
  141. buttonsizer2.Add(self.btn_is, (0,0))
  142. buttonsizer2.Add(self.btn_isnot, (1,0))
  143. buttonsizer2.Add(self.btn_like, (2, 0))
  144. buttonsizer2.Add(self.btn_gt, (0, 1))
  145. buttonsizer2.Add(self.btn_gtis, (1, 1))
  146. buttonsizer2.Add(self.btn_or, (2, 1))
  147. buttonsizer2.Add(self.btn_lt, (0, 2))
  148. buttonsizer2.Add(self.btn_ltis, (1, 2))
  149. buttonsizer2.Add(self.btn_not, (2, 2))
  150. buttonsizer2.Add(self.btn_brackets, (0, 3))
  151. buttonsizer2.Add(self.btn_prc, (1, 3))
  152. buttonsizer2.Add(self.btn_and, (2, 3))
  153. buttonsizer3 = wx.GridSizer(4, 3, 3, 3)
  154. buttonsizer3.Add(self.btn_apply,0,wx.RIGHT,5)
  155. buttonsizer3.Add(self.btn_close,0,wx.RIGHT,5)
  156. buttonsizer4 = wx.BoxSizer(wx.HORIZONTAL)
  157. buttonsizer4.Add(self.btn_uniqesample,0,flag=wx.ALIGN_CENTER_HORIZONTAL,border=5)
  158. buttonsizer4.Add(self.btn_uniqe,0,flag=wx.ALIGN_CENTER_HORIZONTAL,border=5)
  159. hsizer1 = wx.BoxSizer(wx.HORIZONTAL)
  160. #hsizer2 = wx.BoxSizer(wx.HORIZONTAL)
  161. columnsbox = wx.StaticBox(self,-1,"Columns: ")
  162. valuesbox = wx.StaticBox(self,-1,"Values: ")
  163. #hsizer1.Add(wx.StaticText(self,-1, "Unique values: "), border=0, proportion=1)
  164. columnsizer = wx.StaticBoxSizer(columnsbox,wx.VERTICAL)
  165. valuesizer = wx.StaticBoxSizer(valuesbox,wx.VERTICAL)
  166. columnsizer.Add(self.list_columns, flag=wx.EXPAND,)
  167. valuesizer.Add(self.list_values, flag=wx.EXPAND)
  168. self.list_columns.SetMinSize((-1,130))
  169. self.list_values.SetMinSize((-1,100))
  170. valuesizer.Add(buttonsizer4)
  171. hsizer1.Add(columnsizer,border=0, proportion=1)
  172. hsizer1.Add(valuesizer,border=0, proportion=1)
  173. pagesizer.Add(databaseboxsizer,flag=wx.EXPAND,border=5)
  174. pagesizer.Add(hsizer1, 1,flag=wx.EXPAND,border=5)
  175. #pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
  176. #pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
  177. pagesizer.Add(buttonsizer2, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.TOP, border=5)
  178. pagesizer.Add(sqlboxsizer, flag=wx.EXPAND,border=5)
  179. pagesizer.Add(buttonsizer1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.TOP, border=5)
  180. pagesizer.Add(buttonsizer3, proportion=0, flag=wx.TOP, border=5)
  181. self.SetAutoLayout(True)
  182. self.SetSizer(pagesizer)
  183. pagesizer.Fit(self)
  184. #pagesizer.SetSizeHints(self)
  185. self.Layout()
  186. self.Show(True)
  187. def GetColumns(self):
  188. """!Get columns"""
  189. ret = gcmd.RunCommand('db.describe',
  190. quiet = True,
  191. read = True,
  192. flags = 'c',
  193. table = self.tablename,
  194. database = self.database,
  195. driver = self.driver)
  196. for line in ret.splitlines():
  197. # skip ncols and nrows lines
  198. linetype = line.strip().split(":")[0]
  199. if linetype == "ncols" or linetype == "nrows":
  200. continue
  201. num, name, ctype, length = line.strip().split(":")
  202. name.strip()
  203. #self.columns_names.append(name)
  204. self.columns[name] = {'type' : ctype}
  205. def GetUniqueValues(self,event,justsample=False):
  206. vals = []
  207. try:
  208. idx = self.list_columns.GetSelections()[0]
  209. except:
  210. return
  211. self.list_values.Clear()
  212. column = self.list_columns.GetString(idx)
  213. i = 0
  214. querystring = "SELECT %s FROM %s" % (column, self.tablename)
  215. ret = gcmd.RunCommand('db.select',
  216. read = True,
  217. quiet = True,
  218. flags = 'c',
  219. database = self.database,
  220. driver = self.driver,
  221. sql = querystring)
  222. for line in ret.splitlines():
  223. if justsample and i < 256 or \
  224. not justsample:
  225. self.list_values.Insert(line.strip(),0)
  226. else:
  227. break
  228. i += 1
  229. def GetSampleValues(self,event):
  230. self.GetUniqueValues(None,True)
  231. def AddColumnName(self,event):
  232. idx = self.list_columns.GetSelections()[0]
  233. column = self.list_columns.GetString(idx)
  234. self.__addSomething(column)
  235. def AddValue(self,event):
  236. idx = self.list_values.GetSelections()[0]
  237. value = self.list_values.GetString(idx)
  238. idx = self.list_columns.GetSelections()[0]
  239. column = self.list_columns.GetString(idx)
  240. if self.columns[column]['type'].lower().find("chara") > -1:
  241. value = "'%s'" % value
  242. self.__addSomething(value)
  243. def AddMark(self,event):
  244. if event.GetId() == self.btn_is.GetId(): mark = "="
  245. elif event.GetId() == self.btn_isnot.GetId(): mark = "!="
  246. elif event.GetId() == self.btn_like.GetId(): mark = "LIKE"
  247. elif event.GetId() == self.btn_gt.GetId(): mark = ">"
  248. elif event.GetId() == self.btn_gtis.GetId(): mark = ">="
  249. elif event.GetId() == self.btn_lt.GetId(): mark = "<"
  250. elif event.GetId() == self.btn_ltis.GetId(): mark = "<="
  251. elif event.GetId() == self.btn_or.GetId(): mark = "OR"
  252. elif event.GetId() == self.btn_not.GetId(): mark = "NOT"
  253. elif event.GetId() == self.btn_and.GetId(): mark = "AND"
  254. elif event.GetId() == self.btn_brackets.GetId(): mark = "()"
  255. elif event.GetId() == self.btn_prc.GetId(): mark = "%"
  256. self.__addSomething(mark)
  257. def __addSomething(self,what):
  258. sqlstr = self.text_sql.GetValue()
  259. newsqlstr = ''
  260. position = self.text_sql.GetPosition()[0]
  261. selection = self.text_sql.GetSelection()
  262. newsqlstr = sqlstr[:position]
  263. try:
  264. if newsqlstr[-1] != " ":
  265. newsqlstr += " "
  266. except:
  267. pass
  268. newsqlstr += what
  269. newsqlstr += " "+sqlstr[position:]
  270. self.text_sql.SetValue(newsqlstr)
  271. # FIXME: cursor position is messed up
  272. # see also http://trac.wxwidgets.org/ticket/10051
  273. #DEBUG: print "before: %d" % (position)
  274. self.text_sql.SetInsertionPoint(position)
  275. #? self.text_sql.SetInsertionPoint(position+len(what))
  276. #? self.text_sql.SetInsertionPointEnd()
  277. #DEBUG: print "len(what)=%d" % (len(what))
  278. #DEBUG: print "after: %d" % (self.text_sql.GetPosition()[0])
  279. #DEBUG: print " "
  280. def OnApply(self,event):
  281. if self.parent:
  282. try:
  283. self.parent.text_query.SetValue= self.text_sql.GetValue().strip().replace("\n"," ")
  284. except:
  285. pass
  286. def OnVerify(self,event):
  287. if self.text_sql.GetValue():
  288. querystring = "SELECT * FROM %s WHERE %s" % \
  289. (self.tablename,
  290. self.text_sql.GetValue().strip().replace("\n"," "))
  291. # FIXME: LOG
  292. # print self.text_sql.GetValue().strip().replace("\n"," "), "not correct!"
  293. print "Testing [%s] ..." % (querystring)
  294. ret, msg = gcmd.RunCommand('db.select',
  295. getErrorMsg = True,
  296. verbose = True,
  297. flags = 't',
  298. driver = self.driver,
  299. database = self.database,
  300. sql = querystring)
  301. print msg, " "
  302. if ret == 0:
  303. pass
  304. def OnClear(self, event):
  305. self.text_sql.SetValue("")
  306. def OnClose(self,event):
  307. self.Destroy()
  308. if __name__ == "__main__":
  309. if len(sys.argv) != 2:
  310. print >>sys.stderr, __doc__
  311. sys.exit()
  312. app = wx.App(0)
  313. sqlb = SQLFrame(None, -1, 'SQL Builder',sys.argv[1])
  314. app.MainLoop()