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.
 
 
 
 
 
 

408 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
  127. if not getattr(webnotes.local, "reportview_tables", None):
  128. webnotes.local.reportview_tables = []
  129. for f in filters:
  130. if isinstance(f, basestring):
  131. conditions.append(f)
  132. else:
  133. tname = ('`tab' + f[0] + '`')
  134. if not tname in webnotes.local.reportview_tables:
  135. webnotes.local.reportview_tables.append(tname)
  136. # prepare in condition
  137. if f[2] in ['in', 'not in']:
  138. opts = ["'" + t.strip().replace("'", "\\'") + "'" for t in f[3].split(',')]
  139. f[3] = "(" + ', '.join(opts) + ")"
  140. conditions.append(tname + '.' + f[1] + " " + f[2] + " " + f[3])
  141. else:
  142. if isinstance(f[3], basestring):
  143. f[3] = "'" + f[3].replace("'", "\\'") + "'"
  144. conditions.append(tname + '.' + f[1] + " " + f[2] + " " + f[3])
  145. else:
  146. conditions.append('ifnull(' + tname + '.' + f[1] + ",0) " + f[2] \
  147. + " " + cstr(f[3]))
  148. def build_match_conditions(doctype, fields=None, as_condition=True):
  149. """add match conditions if applicable"""
  150. match_filters = {}
  151. match_conditions = []
  152. if not getattr(webnotes.local, "reportview_tables", None) \
  153. or not getattr(webnotes.local, "reportview_doctypes", None):
  154. webnotes.local.reportview_tables = get_tables(doctype, fields)
  155. load_doctypes()
  156. # get restrictions
  157. restrictions = webnotes.defaults.get_restrictions()
  158. if restrictions:
  159. fields_to_check = webnotes.local.reportview_doctypes[doctype].get_restricted_fields(restrictions.keys())
  160. if doctype in restrictions:
  161. fields_to_check.append(webnotes._dict({"fieldname":"name", "options":doctype}))
  162. # check in links
  163. for df in fields_to_check:
  164. if as_condition:
  165. match_conditions.append('`tab{doctype}`.{fieldname} in ({values})'.format(doctype=doctype,
  166. fieldname=df.fieldname,
  167. values=", ".join([('"'+v.replace('"', '\"')+'"') for v in restrictions[df.options]])))
  168. else:
  169. match_filters.setdefault(df.fieldname, [])
  170. match_filters[df.fieldname]= restrictions[df.options]
  171. # add owner match
  172. owner_match = True
  173. for p in webnotes.get_user_perms(webnotes.local.reportview_doctypes[doctype], "read"):
  174. if not (p.match and p.match=="owner"):
  175. owner_match = False
  176. break
  177. if owner_match:
  178. match_conditions.append('`tab{doctype}`.`owner`="{user}"'.format(doctype=doctype,
  179. user=webnotes.local.session.user))
  180. match_filters["owner"] = [webnotes.local.session.user]
  181. if as_condition:
  182. conditions = " and ".join(match_conditions)
  183. doctype_conditions = get_doctype_conditions(doctype)
  184. if doctype_conditions:
  185. conditions += ' and ' + doctype_conditions if conditions else doctype_conditions
  186. return conditions
  187. else:
  188. return match_filters
  189. def get_doctype_conditions(doctype):
  190. from webnotes.model.code import load_doctype_module
  191. module = load_doctype_module(doctype)
  192. if module and hasattr(module, 'get_match_conditions'):
  193. return getattr(module, 'get_match_conditions')()
  194. def get_tables(doctype, fields):
  195. """extract tables from fields"""
  196. tables = ['`tab' + doctype + '`']
  197. # add tables from fields
  198. if fields:
  199. for f in fields:
  200. if "." not in f: continue
  201. table_name = f.split('.')[0]
  202. if table_name.lower().startswith('group_concat('):
  203. table_name = table_name[13:]
  204. if table_name.lower().startswith('ifnull('):
  205. table_name = table_name[7:]
  206. if not table_name[0]=='`':
  207. table_name = '`' + table_name + '`'
  208. if not table_name in tables:
  209. tables.append(table_name)
  210. return tables
  211. @webnotes.whitelist()
  212. def save_report():
  213. """save report"""
  214. from webnotes.model.doc import Document
  215. data = webnotes.local.form_dict
  216. if webnotes.conn.exists('Report', data['name']):
  217. d = Document('Report', data['name'])
  218. else:
  219. d = Document('Report')
  220. d.report_name = data['name']
  221. d.ref_doctype = data['doctype']
  222. d.report_type = "Report Builder"
  223. d.json = data['json']
  224. webnotes.bean([d]).save()
  225. webnotes.msgprint("%s saved." % d.name)
  226. return d.name
  227. @webnotes.whitelist()
  228. def export_query():
  229. """export from report builder"""
  230. # TODO: validate use is allowed to export
  231. verify_export_allowed()
  232. ret = execute(**get_form_params())
  233. columns = [x[0] for x in webnotes.conn.get_description()]
  234. data = [['Sr'] + get_labels(columns),]
  235. # flatten dict
  236. cnt = 1
  237. for row in ret:
  238. flat = [cnt,]
  239. for c in columns:
  240. flat.append(row.get(c))
  241. data.append(flat)
  242. cnt += 1
  243. # convert to csv
  244. from cStringIO import StringIO
  245. import csv
  246. f = StringIO()
  247. writer = csv.writer(f)
  248. for r in data:
  249. # encode only unicode type strings and not int, floats etc.
  250. writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r))
  251. f.seek(0)
  252. webnotes.response['result'] = unicode(f.read(), 'utf-8')
  253. webnotes.response['type'] = 'csv'
  254. webnotes.response['doctype'] = [t[4:-1] for t in webnotes.local.reportview_tables][0]
  255. def verify_export_allowed():
  256. """throw exception if user is not allowed to export"""
  257. webnotes.local.reportview_roles = webnotes.get_roles()
  258. if not ('Administrator' in webnotes.local.reportview_roles or \
  259. 'System Manager' in webnotes.local.reportview_roles or \
  260. 'Report Manager' in webnotes.local.reportview_roles):
  261. raise webnotes.PermissionError
  262. def get_labels(columns):
  263. """get column labels based on column names"""
  264. label_dict = {}
  265. for doctype in webnotes.local.reportview_doctypes:
  266. for d in webnotes.local.reportview_doctypes[doctype]:
  267. if d.doctype=='DocField' and d.fieldname:
  268. label_dict[d.fieldname] = d.label
  269. return map(lambda x: label_dict.get(x, x.title()), columns)
  270. @webnotes.whitelist()
  271. def delete_items():
  272. """delete selected items"""
  273. import json
  274. from webnotes.model.code import get_obj
  275. il = json.loads(webnotes.form_dict.get('items'))
  276. doctype = webnotes.form_dict.get('doctype')
  277. for d in il:
  278. try:
  279. dt_obj = get_obj(doctype, d)
  280. if hasattr(dt_obj, 'on_trash'):
  281. dt_obj.on_trash()
  282. webnotes.delete_doc(doctype, d)
  283. except Exception, e:
  284. webnotes.errprint(webnotes.getTraceback())
  285. pass
  286. @webnotes.whitelist()
  287. def get_stats(stats, doctype):
  288. """get tag info"""
  289. import json
  290. tags = json.loads(stats)
  291. stats = {}
  292. columns = get_table_columns(doctype)
  293. for tag in tags:
  294. if not tag in columns: continue
  295. tagcount = execute(doctype, fields=[tag, "count(*)"],
  296. filters=["ifnull(%s,'')!=''" % tag], group_by=tag, as_list=True)
  297. if tag=='_user_tags':
  298. stats[tag] = scrub_user_tags(tagcount)
  299. else:
  300. stats[tag] = tagcount
  301. return stats
  302. def scrub_user_tags(tagcount):
  303. """rebuild tag list for tags"""
  304. rdict = {}
  305. tagdict = dict(tagcount)
  306. for t in tagdict:
  307. alltags = t.split(',')
  308. for tag in alltags:
  309. if tag:
  310. if not tag in rdict:
  311. rdict[tag] = 0
  312. rdict[tag] += tagdict[t]
  313. rlist = []
  314. for tag in rdict:
  315. rlist.append([tag, rdict[tag]])
  316. return rlist
  317. def get_table_columns(table):
  318. res = webnotes.conn.sql("DESC `tab%s`" % table, as_dict=1)
  319. if res: return [r['Field'] for r in res]
  320. # used in building query in queries.py
  321. def get_match_cond(doctype, searchfield = 'name'):
  322. cond = build_match_conditions(doctype)
  323. if cond:
  324. cond = ' and ' + cond
  325. else:
  326. cond = ''
  327. return cond