You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

query_builder.py 7.5 KiB

12 years ago
13 years ago
13 years ago
13 years ago
13 years ago
12 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import webnotes
  5. out = webnotes.response
  6. from webnotes.utils import cint
  7. import webnotes.defaults
  8. def get_sql_tables(q):
  9. if q.find('WHERE') != -1:
  10. tl = q.split('FROM')[1].split('WHERE')[0].split(',')
  11. elif q.find('GROUP BY') != -1:
  12. tl = q.split('FROM')[1].split('GROUP BY')[0].split(',')
  13. else:
  14. tl = q.split('FROM')[1].split('ORDER BY')[0].split(',')
  15. return [t.strip().strip('`')[3:] for t in tl]
  16. def get_parent_dt(dt):
  17. pdt = ''
  18. if webnotes.conn.sql('select name from `tabDocType` where istable=1 and name="%s"' % dt):
  19. import webnotes.model.meta
  20. return webnotes.model.meta.get_parent_dt(dt)
  21. return pdt
  22. def get_sql_meta(tl):
  23. std_columns = {
  24. 'owner':('Owner', '', '', '100'),
  25. 'creation':('Created on', 'Date', '', '100'),
  26. 'modified':('Last modified on', 'Date', '', '100'),
  27. 'modified_by':('Modified By', '', '', '100')
  28. }
  29. meta = {}
  30. for dt in tl:
  31. meta[dt] = std_columns.copy()
  32. # for table doctype, the ID is the parent id
  33. pdt = get_parent_dt(dt)
  34. if pdt:
  35. meta[dt]['parent'] = ('ID', 'Link', pdt, '200')
  36. # get the field properties from DocField
  37. res = webnotes.conn.sql("select fieldname, label, fieldtype, options, width from tabDocField where parent='%s'" % dt)
  38. for r in res:
  39. if r[0]:
  40. meta[dt][r[0]] = (r[1], r[2], r[3], r[4]);
  41. # name
  42. meta[dt]['name'] = ('ID', 'Link', dt, '200')
  43. return meta
  44. def add_match_conditions(q, tl):
  45. from webnotes.widgets.reportview import build_match_conditions
  46. sl = []
  47. for dt in tl:
  48. s = build_match_conditions(dt)
  49. if s:
  50. sl.append(s)
  51. # insert the conditions
  52. if sl:
  53. condition_st = q.find('WHERE')!=-1 and ' AND ' or ' WHERE '
  54. condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT'
  55. condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end
  56. if q.find('ORDER BY')!=-1 or q.find('LIMIT')!=-1 or q.find('GROUP BY')!=-1: # if query continues beyond conditions
  57. q = q.split(condition_end)
  58. q = q[0] + condition_st + '(' + ' OR '.join(sl) + ') ' + condition_end + q[1]
  59. else:
  60. q = q + condition_st + '(' + ' OR '.join(sl) + ')'
  61. return q
  62. def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[], filter_values={}, query='', from_export=0):
  63. col_idx, i, out, style, header_html, footer_html, page_template = {}, 0, None, [], '', '', ''
  64. for c in colnames:
  65. col_idx[c] = i
  66. i+=1
  67. # load globals (api)
  68. from webnotes import *
  69. from webnotes.utils import *
  70. from webnotes.model.doc import *
  71. set = webnotes.conn.set
  72. get_value = webnotes.conn.get_value
  73. convert_to_lists = webnotes.conn.convert_to_lists
  74. NEWLINE = '\n'
  75. exec str(code)
  76. if out!=None:
  77. res = out
  78. return res, style, header_html, footer_html, page_template
  79. def guess_type(m):
  80. """
  81. Returns fieldtype depending on the MySQLdb Description
  82. """
  83. import MySQLdb
  84. if m in MySQLdb.NUMBER:
  85. return 'Currency'
  86. elif m in MySQLdb.DATE:
  87. return 'Date'
  88. else:
  89. return 'Data'
  90. def build_description_simple():
  91. colnames, coltypes, coloptions, colwidths = [], [], [], []
  92. for m in webnotes.conn.get_description():
  93. colnames.append(m[0])
  94. coltypes.append(guess_type[m[0]])
  95. coloptions.append('')
  96. colwidths.append('100')
  97. return colnames, coltypes, coloptions, colwidths
  98. def build_description_standard(meta, tl):
  99. desc = webnotes.conn.get_description()
  100. colnames, coltypes, coloptions, colwidths = [], [], [], []
  101. # merged metadata - used if we are unable to
  102. # get both the table name and field name from
  103. # the description - in case of joins
  104. merged_meta = {}
  105. for d in meta:
  106. merged_meta.update(meta[d])
  107. for f in desc:
  108. fn, dt = f[0], ''
  109. if '.' in fn:
  110. dt, fn = fn.split('.')
  111. if (not dt) and merged_meta.get(fn):
  112. # no "AS" given, find type from merged description
  113. desc = merged_meta[fn]
  114. colnames.append(desc[0] or fn)
  115. coltypes.append(desc[1] or '')
  116. coloptions.append(desc[2] or '')
  117. colwidths.append(desc[3] or '100')
  118. elif meta.get(dt,{}).has_key(fn):
  119. # type specified for a multi-table join
  120. # usually from Report Builder
  121. desc = meta[dt][fn]
  122. colnames.append(desc[0] or fn)
  123. coltypes.append(desc[1] or '')
  124. coloptions.append(desc[2] or '')
  125. colwidths.append(desc[3] or '100')
  126. else:
  127. # nothing found
  128. # guess
  129. colnames.append(fn)
  130. coltypes.append(guess_type(f[1]))
  131. coloptions.append('')
  132. colwidths.append('100')
  133. return colnames, coltypes, coloptions, colwidths
  134. @webnotes.whitelist()
  135. def runquery(q='', ret=0, from_export=0):
  136. import webnotes.utils
  137. formatted = cint(webnotes.form_dict.get('formatted'))
  138. # CASE A: Simple Query
  139. # --------------------
  140. if webnotes.form_dict.get('simple_query') or webnotes.form_dict.get('is_simple'):
  141. if not q: q = webnotes.form_dict.get('simple_query') or webnotes.form_dict.get('query')
  142. if q.split()[0].lower() != 'select':
  143. raise Exception, 'Query must be a SELECT'
  144. as_dict = cint(webnotes.form_dict.get('as_dict'))
  145. res = webnotes.conn.sql(q, as_dict = as_dict, as_list = not as_dict, formatted=formatted)
  146. # build colnames etc from metadata
  147. colnames, coltypes, coloptions, colwidths = [], [], [], []
  148. # CASE B: Standard Query
  149. # -----------------------
  150. else:
  151. if not q: q = webnotes.form_dict.get('query')
  152. tl = get_sql_tables(q)
  153. meta = get_sql_meta(tl)
  154. q = add_match_conditions(q, tl)
  155. # replace special variables
  156. q = q.replace('__user', webnotes.session.user)
  157. q = q.replace('__today', webnotes.utils.nowdate())
  158. res = webnotes.conn.sql(q, as_list=1, formatted=formatted)
  159. colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl)
  160. # run server script
  161. # -----------------
  162. style, header_html, footer_html, page_template = '', '', '', ''
  163. out['colnames'] = colnames
  164. out['coltypes'] = coltypes
  165. out['coloptions'] = coloptions
  166. out['colwidths'] = colwidths
  167. out['header_html'] = header_html
  168. out['footer_html'] = footer_html
  169. out['page_template'] = page_template
  170. if style:
  171. out['style'] = style
  172. # just the data - return
  173. if ret==1:
  174. return res
  175. out['values'] = res
  176. # return num of entries
  177. qm = webnotes.form_dict.get('query_max') or ''
  178. if qm and qm.strip():
  179. if qm.split()[0].lower() != 'select':
  180. raise Exception, 'Query (Max) must be a SELECT'
  181. if not webnotes.form_dict.get('simple_query'):
  182. qm = add_match_conditions(qm, tl)
  183. out['n_values'] = webnotes.utils.cint(webnotes.conn.sql(qm)[0][0])
  184. @webnotes.whitelist()
  185. def runquery_csv():
  186. global out
  187. # run query
  188. res = runquery(from_export = 1)
  189. q = webnotes.form_dict.get('query')
  190. rep_name = webnotes.form_dict.get('report_name')
  191. if not webnotes.form_dict.get('simple_query'):
  192. # Report Name
  193. if not rep_name:
  194. rep_name = get_sql_tables(q)[0]
  195. if not rep_name: rep_name = 'DataExport'
  196. # Headings
  197. heads = []
  198. rows = [[rep_name], out['colnames']] + out['values']
  199. from cStringIO import StringIO
  200. import csv
  201. f = StringIO()
  202. writer = csv.writer(f)
  203. for r in rows:
  204. # encode only unicode type strings and not int, floats etc.
  205. writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r))
  206. f.seek(0)
  207. out['result'] = unicode(f.read(), 'utf-8')
  208. out['type'] = 'csv'
  209. out['doctype'] = rep_name
  210. def add_limit_to_query(query, args):
  211. """
  212. Add limit condition to query
  213. can be used by methods called in listing to add limit condition
  214. """
  215. if args.get('limit_page_length'):
  216. query += """
  217. limit %(limit_start)s, %(limit_page_length)s"""
  218. import webnotes.utils
  219. args['limit_start'] = webnotes.utils.cint(args.get('limit_start'))
  220. args['limit_page_length'] = webnotes.utils.cint(args.get('limit_page_length'))
  221. return query, args