您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

349 行
9.8 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. import webnotes
  23. form = webnotes.form
  24. session = webnotes.session
  25. sql = webnotes.conn.sql
  26. out = webnotes.response
  27. from webnotes.utils import cint
  28. # Get, scrub metadata
  29. # ====================================================================
  30. def get_sql_tables(q):
  31. if q.find('WHERE') != -1:
  32. tl = q.split('FROM')[1].split('WHERE')[0].split(',')
  33. elif q.find('GROUP BY') != -1:
  34. tl = q.split('FROM')[1].split('GROUP BY')[0].split(',')
  35. else:
  36. tl = q.split('FROM')[1].split('ORDER BY')[0].split(',')
  37. return [t.strip().strip('`')[3:] for t in tl]
  38. def get_parent_dt(dt):
  39. pdt = ''
  40. if sql('select name from `tabDocType` where istable=1 and name="%s"' % dt):
  41. import webnotes.model.meta
  42. return webnotes.model.meta.get_parent_dt(dt)
  43. return pdt
  44. def get_sql_meta(tl):
  45. std_columns = {
  46. 'owner':('Owner', '', '', '100'),
  47. 'creation':('Created on', 'Date', '', '100'),
  48. 'modified':('Last modified on', 'Date', '', '100'),
  49. 'modified_by':('Modified By', '', '', '100')
  50. }
  51. meta = {}
  52. for dt in tl:
  53. meta[dt] = std_columns.copy()
  54. # for table doctype, the ID is the parent id
  55. pdt = get_parent_dt(dt)
  56. if pdt:
  57. meta[dt]['parent'] = ('ID', 'Link', pdt, '200')
  58. # get the field properties from DocField
  59. res = sql("select fieldname, label, fieldtype, options, width from tabDocField where parent='%s'" % dt)
  60. for r in res:
  61. if r[0]:
  62. meta[dt][r[0]] = (r[1], r[2], r[3], r[4]);
  63. # name
  64. meta[dt]['name'] = ('ID', 'Link', dt, '200')
  65. return meta
  66. # Additional conditions to fulfill match permission rules
  67. # ====================================================================
  68. def getmatchcondition(dt, ud, ur):
  69. res = sql("SELECT `role`, `match` FROM tabDocPerm WHERE parent = '%s' AND (`read`=1) AND permlevel = 0" % dt)
  70. cond = []
  71. for r in res:
  72. if r[0] in ur: # role applicable to user
  73. if r[1]:
  74. defvalues = ud.get(r[1],['_NA'])
  75. for d in defvalues:
  76. cond.append('`tab%s`.`%s`="%s"' % (dt, r[1], d))
  77. else: # nomatch i.e. full read rights
  78. return ''
  79. return ' OR '.join(cond)
  80. def add_match_conditions(q, tl, ur, ud):
  81. sl = []
  82. for dt in tl:
  83. s = getmatchcondition(dt, ud, ur)
  84. if s:
  85. sl.append(s)
  86. # insert the conditions
  87. if sl:
  88. condition_st = q.find('WHERE')!=-1 and ' AND ' or ' WHERE '
  89. condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT'
  90. condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end
  91. if q.find('ORDER BY')!=-1 or q.find('LIMIT')!=-1 or q.find('GROUP BY')!=-1: # if query continues beyond conditions
  92. q = q.split(condition_end)
  93. q = q[0] + condition_st + '(' + ' OR '.join(sl) + ') ' + condition_end + q[1]
  94. else:
  95. q = q + condition_st + '(' + ' OR '.join(sl) + ')'
  96. return q
  97. # execute server-side script from Search Criteria
  98. # ====================================================================
  99. def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[], filter_values={}, query='', from_export=0):
  100. col_idx, i, out, style, header_html, footer_html, page_template = {}, 0, None, [], '', '', ''
  101. for c in colnames:
  102. col_idx[c] = i
  103. i+=1
  104. # load globals (api)
  105. from webnotes import *
  106. from webnotes.utils import *
  107. from webnotes.model.doc import *
  108. from webnotes.model.doclist import getlist
  109. from webnotes.model.db_schema import updatedb
  110. from webnotes.model.code import get_obj
  111. set = webnotes.conn.set
  112. sql = webnotes.conn.sql
  113. get_value = webnotes.conn.get_value
  114. convert_to_lists = webnotes.conn.convert_to_lists
  115. NEWLINE = '\n'
  116. exec str(code)
  117. if out!=None:
  118. res = out
  119. return res, style, header_html, footer_html, page_template
  120. # ====================================================================
  121. def guess_type(m):
  122. """
  123. Returns fieldtype depending on the MySQLdb Description
  124. """
  125. import MySQLdb
  126. if m in MySQLdb.NUMBER:
  127. return 'Currency'
  128. elif m in MySQLdb.DATE:
  129. return 'Date'
  130. else:
  131. return 'Data'
  132. def build_description_simple():
  133. colnames, coltypes, coloptions, colwidths = [], [], [], []
  134. for m in webnotes.conn.get_description():
  135. colnames.append(m[0])
  136. coltypes.append(guess_type[m[0]])
  137. coloptions.append('')
  138. colwidths.append('100')
  139. return colnames, coltypes, coloptions, colwidths
  140. # ====================================================================
  141. def build_description_standard(meta, tl):
  142. desc = webnotes.conn.get_description()
  143. colnames, coltypes, coloptions, colwidths = [], [], [], []
  144. # merged metadata - used if we are unable to
  145. # get both the table name and field name from
  146. # the description - in case of joins
  147. merged_meta = {}
  148. for d in meta:
  149. merged_meta.update(meta[d])
  150. for f in desc:
  151. fn, dt = f[0], ''
  152. if '.' in fn:
  153. dt, fn = fn.split('.')
  154. if (not dt) and merged_meta.get(fn):
  155. # no "AS" given, find type from merged description
  156. desc = merged_meta[fn]
  157. colnames.append(desc[0] or fn)
  158. coltypes.append(desc[1] or '')
  159. coloptions.append(desc[2] or '')
  160. colwidths.append(desc[3] or '100')
  161. elif meta.get(dt,{}).has_key(fn):
  162. # type specified for a multi-table join
  163. # usually from Report Builder
  164. desc = meta[dt][fn]
  165. colnames.append(desc[0] or fn)
  166. coltypes.append(desc[1] or '')
  167. coloptions.append(desc[2] or '')
  168. colwidths.append(desc[3] or '100')
  169. else:
  170. # nothing found
  171. # guess
  172. colnames.append(fn)
  173. coltypes.append(guess_type(f[1]))
  174. coloptions.append('')
  175. colwidths.append('100')
  176. return colnames, coltypes, coloptions, colwidths
  177. # Entry Point - Run the query
  178. # ====================================================================
  179. @webnotes.whitelist(allow_guest=True)
  180. def runquery(q='', ret=0, from_export=0):
  181. import webnotes.utils
  182. formatted = cint(form.getvalue('formatted'))
  183. # CASE A: Simple Query
  184. # --------------------
  185. if form.getvalue('simple_query') or form.getvalue('is_simple'):
  186. if not q: q = form.getvalue('simple_query') or form.getvalue('query')
  187. if q.split()[0].lower() != 'select':
  188. raise Exception, 'Query must be a SELECT'
  189. as_dict = cint(form.getvalue('as_dict'))
  190. res = sql(q, as_dict = as_dict, as_list = not as_dict, formatted=formatted)
  191. # build colnames etc from metadata
  192. colnames, coltypes, coloptions, colwidths = [], [], [], []
  193. # CASE B: Standard Query
  194. # -----------------------
  195. else:
  196. if not q: q = form.getvalue('query')
  197. tl = get_sql_tables(q)
  198. meta = get_sql_meta(tl)
  199. q = add_match_conditions(q, tl, webnotes.user.roles, webnotes.user.get_defaults())
  200. # replace special variables
  201. q = q.replace('__user', session['user'])
  202. q = q.replace('__today', webnotes.utils.nowdate())
  203. res = sql(q, as_list=1, formatted=formatted)
  204. colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl)
  205. # run server script
  206. # -----------------
  207. style, header_html, footer_html, page_template = '', '', '', ''
  208. if form.has_key('sc_id') and form.getvalue('sc_id'):
  209. sc_id = form.getvalue('sc_id')
  210. from webnotes.model.code import get_code
  211. sc_details = webnotes.conn.sql("select module, standard, server_script from `tabSearch Criteria` where name=%s", sc_id)[0]
  212. if sc_details[1]!='No':
  213. code = get_code(sc_details[0], 'Search Criteria', sc_id, 'py')
  214. else:
  215. code = sc_details[2]
  216. if code:
  217. filter_values = form.has_key('filter_values') and eval(form.getvalue('filter_values','')) or {}
  218. res, style, header_html, footer_html, page_template = exec_report(code, res, colnames, colwidths, coltypes, coloptions, filter_values, q, from_export)
  219. out['colnames'] = colnames
  220. out['coltypes'] = coltypes
  221. out['coloptions'] = coloptions
  222. out['colwidths'] = colwidths
  223. out['header_html'] = header_html
  224. out['footer_html'] = footer_html
  225. out['page_template'] = page_template
  226. if style:
  227. out['style'] = style
  228. # just the data - return
  229. if ret==1:
  230. return res
  231. out['values'] = res
  232. # return num of entries
  233. qm = form.has_key('query_max') and form.getvalue('query_max') or ''
  234. if qm and qm.strip():
  235. if qm.split()[0].lower() != 'select':
  236. raise Exception, 'Query (Max) must be a SELECT'
  237. if not form.has_key('simple_query'):
  238. qm = add_match_conditions(qm, tl, webnotes.user.roles, webnotes.user.defaults)
  239. out['n_values'] = webnotes.utils.cint(sql(qm)[0][0])
  240. @webnotes.whitelist()
  241. def runquery_csv():
  242. global out
  243. # run query
  244. res = runquery(from_export = 1)
  245. q = form.getvalue('query')
  246. rep_name = form.getvalue('report_name')
  247. if not form.has_key('simple_query'):
  248. # Report Name
  249. if not rep_name:
  250. rep_name = get_sql_tables(q)[0]
  251. if not rep_name: rep_name = 'DataExport'
  252. # Headings
  253. heads = []
  254. rows = [[rep_name], out['colnames']] + out['values']
  255. from cStringIO import StringIO
  256. import csv
  257. f = StringIO()
  258. writer = csv.writer(f)
  259. for r in rows:
  260. for i in xrange(len(r)):
  261. if type(r[i]) is unicode:
  262. r[i] = r[i].encode('utf-8')
  263. writer.writerow(r)
  264. f.seek(0)
  265. out['result'] = f.read()
  266. out['type'] = 'csv'
  267. out['doctype'] = rep_name