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.
 
 
 
 
 
 

398 lines
10 KiB

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