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.
 
 
 
 
 
 

421 lines
12 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. """build query for doclistview and return results"""
  5. import webnotes, json
  6. import webnotes.defaults
  7. @webnotes.whitelist()
  8. def get():
  9. return compress(execute(**get_form_params()))
  10. def get_form_params():
  11. data = webnotes._dict(webnotes.local.form_dict)
  12. del data["cmd"]
  13. if isinstance(data.get("filters"), basestring):
  14. data["filters"] = json.loads(data["filters"])
  15. if isinstance(data.get("fields"), basestring):
  16. data["fields"] = json.loads(data["fields"])
  17. if isinstance(data.get("docstatus"), basestring):
  18. data["docstatus"] = json.loads(data["docstatus"])
  19. return data
  20. def execute(doctype, query=None, filters=None, fields=None, docstatus=None,
  21. group_by=None, order_by=None, limit_start=0, limit_page_length=None,
  22. as_list=False, with_childnames=False, debug=False):
  23. if query:
  24. return run_custom_query(query)
  25. if not filters: filters = []
  26. if not docstatus: docstatus = []
  27. args = prepare_args(doctype, filters, fields, docstatus, group_by, order_by, with_childnames)
  28. args.limit = add_limit(limit_start, limit_page_length)
  29. query = """select %(fields)s from %(tables)s where %(conditions)s
  30. %(group_by)s order by %(order_by)s %(limit)s""" % args
  31. return webnotes.conn.sql(query, as_dict=not as_list, debug=debug)
  32. def prepare_args(doctype, filters, fields, docstatus, group_by, order_by, with_childnames):
  33. webnotes.local.reportview_tables = get_tables(doctype, fields)
  34. load_doctypes()
  35. remove_user_tags(doctype, fields)
  36. conditions = build_conditions(doctype, fields, filters, docstatus)
  37. args = webnotes._dict()
  38. if with_childnames:
  39. for t in webnotes.local.reportview_tables:
  40. if t != "`tab" + doctype + "`":
  41. fields.append(t + ".name as '%s:name'" % t[4:-1])
  42. # query dict
  43. args.tables = ', '.join(webnotes.local.reportview_tables)
  44. args.conditions = ' and '.join(conditions)
  45. args.fields = ', '.join(fields)
  46. args.order_by = order_by or webnotes.local.reportview_tables[0] + '.modified desc'
  47. args.group_by = group_by and (" group by " + group_by) or ""
  48. check_sort_by_table(args.order_by)
  49. return args
  50. def compress(data):
  51. """separate keys and values"""
  52. if not data: return data
  53. values = []
  54. keys = data[0].keys()
  55. for row in data:
  56. new_row = []
  57. for key in keys:
  58. new_row.append(row[key])
  59. values.append(new_row)
  60. return {
  61. "keys": keys,
  62. "values": values
  63. }
  64. def check_sort_by_table(sort_by):
  65. """check atleast 1 column selected from the sort by table """
  66. if "." in sort_by:
  67. tbl = sort_by.split('.')[0]
  68. if tbl not in webnotes.local.reportview_tables:
  69. if tbl.startswith('`'):
  70. tbl = tbl[4:-1]
  71. webnotes.msgprint("Please select atleast 1 column from '%s' to sort"\
  72. % tbl, raise_exception=1)
  73. def run_custom_query(query):
  74. """run custom query"""
  75. if '%(key)s' in query:
  76. query = query.replace('%(key)s', 'name')
  77. return webnotes.conn.sql(query, as_dict=1)
  78. def load_doctypes():
  79. """load all doctypes and roles"""
  80. import webnotes.model.doctype
  81. webnotes.local.reportview_roles = webnotes.get_roles()
  82. if not getattr(webnotes.local, "reportview_doctypes", None):
  83. webnotes.local.reportview_doctypes = {}
  84. for t in webnotes.local.reportview_tables:
  85. if t.startswith('`'):
  86. doctype = t[4:-1]
  87. if not webnotes.has_permission(doctype):
  88. raise webnotes.PermissionError, doctype
  89. webnotes.local.reportview_doctypes[doctype] = webnotes.model.doctype.get(doctype)
  90. def remove_user_tags(doctype, fields):
  91. """remove column _user_tags if not in table"""
  92. columns = get_table_columns(doctype)
  93. del_user_tags = False
  94. del_comments = False
  95. for fld in fields:
  96. if '_user_tags' in fld and not "_user_tags" in columns:
  97. del_user_tags = fld
  98. if '_comments' in fld and not "_comments" in columns:
  99. del_comments = fld
  100. if del_user_tags: del fields[fields.index(del_user_tags)]
  101. if del_comments: del fields[fields.index(del_comments)]
  102. def add_limit(limit_start, limit_page_length):
  103. if limit_page_length:
  104. return 'limit %s, %s' % (limit_start, limit_page_length)
  105. else:
  106. return ''
  107. def build_conditions(doctype, fields, filters, docstatus):
  108. """build conditions"""
  109. if docstatus:
  110. conditions = [webnotes.local.reportview_tables[0] + '.docstatus in (' + ','.join(docstatus) + ')']
  111. else:
  112. # default condition
  113. conditions = [webnotes.local.reportview_tables[0] + '.docstatus < 2']
  114. # make conditions from filters
  115. build_filter_conditions(filters, conditions)
  116. # join parent, child tables
  117. for tname in webnotes.local.reportview_tables[1:]:
  118. conditions.append(tname + '.parent = ' + webnotes.local.reportview_tables[0] + '.name')
  119. # match conditions
  120. match_conditions = build_match_conditions(doctype, fields)
  121. if match_conditions:
  122. conditions.append(match_conditions)
  123. return conditions
  124. def build_filter_conditions(filters, conditions):
  125. """build conditions from user filters"""
  126. from webnotes.utils import cstr, flt
  127. if not getattr(webnotes.local, "reportview_tables", None):
  128. webnotes.local.reportview_tables = []
  129. doclist = {}
  130. for f in filters:
  131. if isinstance(f, basestring):
  132. conditions.append(f)
  133. else:
  134. tname = ('`tab' + f[0] + '`')
  135. if not tname in webnotes.local.reportview_tables:
  136. webnotes.local.reportview_tables.append(tname)
  137. # prepare in condition
  138. if f[2] in ['in', 'not in']:
  139. opts = ["'" + t.strip().replace("'", "\\'") + "'" for t in f[3].split(',')]
  140. f[3] = "(" + ', '.join(opts) + ")"
  141. conditions.append('ifnull(' + tname + '.' + f[1] + ", '') " + f[2] + " " + f[3])
  142. else:
  143. if isinstance(f[3], basestring):
  144. df = webnotes.local.reportview_doctypes[f[0]].get({"doctype": "DocField",
  145. "fieldname": f[1]})
  146. if df and df[0].fieldtype in ["Float", "Int", "Currency", "Percent"]:
  147. val, default_null_val = flt(f[3]), "0"
  148. else:
  149. val, default_null_val = ("'" + f[3].replace("'", "\\'") + "'"), '""'
  150. else:
  151. val, default_null_val = f[3], '0'
  152. conditions.append('ifnull({tname}.{fname}, {default_val}) {operator} {value}'.format(
  153. tname=tname, fname=f[1], default_val=default_null_val, operator=f[2],
  154. value=cstr(val)))
  155. def build_match_conditions(doctype, fields=None, as_condition=True):
  156. """add match conditions if applicable"""
  157. match_filters = {}
  158. match_conditions = []
  159. match = True
  160. if not getattr(webnotes.local, "reportview_tables", None) \
  161. or not getattr(webnotes.local, "reportview_doctypes", None):
  162. webnotes.local.reportview_tables = get_tables(doctype, fields)
  163. load_doctypes()
  164. if not getattr(webnotes.local, "reportview_roles", None):
  165. webnotes.local.reportview_roles = webnotes.get_roles()
  166. for d in webnotes.local.reportview_doctypes[doctype]:
  167. if d.doctype == 'DocPerm' and d.parent == doctype:
  168. if d.role in webnotes.local.reportview_roles:
  169. if d.match: # role applicable
  170. if ':' in d.match:
  171. document_key, default_key = d.match.split(":")
  172. else:
  173. default_key = document_key = d.match
  174. for v in webnotes.defaults.get_user_default_as_list(default_key, \
  175. webnotes.session.user) or ["** No Match **"]:
  176. if as_condition:
  177. match_conditions.append('`tab%s`.%s="%s"' % (doctype,
  178. document_key, v))
  179. else:
  180. if v:
  181. match_filters.setdefault(document_key, [])
  182. if v not in match_filters[document_key]:
  183. match_filters[document_key].append(v)
  184. elif d.read == 1 and d.permlevel == 0:
  185. # don't restrict if another read permission at level 0
  186. # exists without a match restriction
  187. match = False
  188. match_filters = {}
  189. if as_condition:
  190. conditions = ""
  191. if match_conditions and match:
  192. conditions = '('+ ' or '.join(match_conditions) +')'
  193. doctype_conditions = get_doctype_conditions(doctype)
  194. if doctype_conditions:
  195. conditions += ' and ' + doctype_conditions if conditions else doctype_conditions
  196. return conditions
  197. else:
  198. return match_filters
  199. def get_doctype_conditions(doctype):
  200. from webnotes.model.code import load_doctype_module
  201. module = load_doctype_module(doctype)
  202. if module and hasattr(module, 'get_match_conditions'):
  203. return getattr(module, 'get_match_conditions')()
  204. def get_tables(doctype, fields):
  205. """extract tables from fields"""
  206. tables = ['`tab' + doctype + '`']
  207. # add tables from fields
  208. if fields:
  209. for f in fields:
  210. if "." not in f: continue
  211. table_name = f.split('.')[0]
  212. if table_name.lower().startswith('group_concat('):
  213. table_name = table_name[13:]
  214. if table_name.lower().startswith('ifnull('):
  215. table_name = table_name[7:]
  216. if not table_name[0]=='`':
  217. table_name = '`' + table_name + '`'
  218. if not table_name in tables:
  219. tables.append(table_name)
  220. return tables
  221. @webnotes.whitelist()
  222. def save_report():
  223. """save report"""
  224. from webnotes.model.doc import Document
  225. data = webnotes.local.form_dict
  226. if webnotes.conn.exists('Report', data['name']):
  227. d = Document('Report', data['name'])
  228. else:
  229. d = Document('Report')
  230. d.report_name = data['name']
  231. d.ref_doctype = data['doctype']
  232. d.report_type = "Report Builder"
  233. d.json = data['json']
  234. webnotes.bean([d]).save()
  235. webnotes.msgprint("%s saved." % d.name)
  236. return d.name
  237. @webnotes.whitelist()
  238. def export_query():
  239. """export from report builder"""
  240. # TODO: validate use is allowed to export
  241. verify_export_allowed()
  242. ret = execute(**get_form_params())
  243. columns = [x[0] for x in webnotes.conn.get_description()]
  244. data = [['Sr'] + get_labels(columns),]
  245. # flatten dict
  246. cnt = 1
  247. for row in ret:
  248. flat = [cnt,]
  249. for c in columns:
  250. flat.append(row.get(c))
  251. data.append(flat)
  252. cnt += 1
  253. # convert to csv
  254. from cStringIO import StringIO
  255. import csv
  256. f = StringIO()
  257. writer = csv.writer(f)
  258. for r in data:
  259. # encode only unicode type strings and not int, floats etc.
  260. writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r))
  261. f.seek(0)
  262. webnotes.response['result'] = unicode(f.read(), 'utf-8')
  263. webnotes.response['type'] = 'csv'
  264. webnotes.response['doctype'] = [t[4:-1] for t in webnotes.local.reportview_tables][0]
  265. def verify_export_allowed():
  266. """throw exception if user is not allowed to export"""
  267. webnotes.local.reportview_roles = webnotes.get_roles()
  268. if not ('Administrator' in webnotes.local.reportview_roles or \
  269. 'System Manager' in webnotes.local.reportview_roles or \
  270. 'Report Manager' in webnotes.local.reportview_roles):
  271. raise webnotes.PermissionError
  272. def get_labels(columns):
  273. """get column labels based on column names"""
  274. label_dict = {}
  275. for doctype in webnotes.local.reportview_doctypes:
  276. for d in webnotes.local.reportview_doctypes[doctype]:
  277. if d.doctype=='DocField' and d.fieldname:
  278. label_dict[d.fieldname] = d.label
  279. return map(lambda x: label_dict.get(x, x.title()), columns)
  280. @webnotes.whitelist()
  281. def delete_items():
  282. """delete selected items"""
  283. import json
  284. from webnotes.model import delete_doc
  285. from webnotes.model.code import get_obj
  286. il = json.loads(webnotes.form_dict.get('items'))
  287. doctype = webnotes.form_dict.get('doctype')
  288. for d in il:
  289. try:
  290. dt_obj = get_obj(doctype, d)
  291. if hasattr(dt_obj, 'on_trash'):
  292. dt_obj.on_trash()
  293. delete_doc(doctype, d)
  294. except Exception, e:
  295. webnotes.errprint(webnotes.getTraceback())
  296. pass
  297. @webnotes.whitelist()
  298. def get_stats(stats, doctype):
  299. """get tag info"""
  300. import json
  301. tags = json.loads(stats)
  302. stats = {}
  303. columns = get_table_columns(doctype)
  304. for tag in tags:
  305. if not tag in columns: continue
  306. tagcount = execute(doctype, fields=[tag, "count(*)"],
  307. filters=["ifnull(%s,'')!=''" % tag], group_by=tag, as_list=True)
  308. if tag=='_user_tags':
  309. stats[tag] = scrub_user_tags(tagcount)
  310. else:
  311. stats[tag] = tagcount
  312. return stats
  313. def scrub_user_tags(tagcount):
  314. """rebuild tag list for tags"""
  315. rdict = {}
  316. tagdict = dict(tagcount)
  317. for t in tagdict:
  318. alltags = t.split(',')
  319. for tag in alltags:
  320. if tag:
  321. if not tag in rdict:
  322. rdict[tag] = 0
  323. rdict[tag] += tagdict[t]
  324. rlist = []
  325. for tag in rdict:
  326. rlist.append([tag, rdict[tag]])
  327. return rlist
  328. def get_table_columns(table):
  329. res = webnotes.conn.sql("DESC `tab%s`" % table, as_dict=1)
  330. if res: return [r['Field'] for r in res]
  331. # used in building query in queries.py
  332. def get_match_cond(doctype, searchfield = 'name'):
  333. cond = build_match_conditions(doctype)
  334. if cond:
  335. cond = ' and ' + cond
  336. else:
  337. cond = ''
  338. return cond