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.

reportview.py 12 KiB

13 年之前
13 年之前
13 年之前
13 年之前
13 年之前
12 年之前
13 年之前
12 年之前
12 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. import webnotes.permissions
  8. @webnotes.whitelist()
  9. def get():
  10. return compress(execute(**get_form_params()))
  11. def get_form_params():
  12. data = webnotes._dict(webnotes.local.form_dict)
  13. del data["cmd"]
  14. if isinstance(data.get("filters"), basestring):
  15. data["filters"] = json.loads(data["filters"])
  16. if isinstance(data.get("fields"), basestring):
  17. data["fields"] = json.loads(data["fields"])
  18. if isinstance(data.get("docstatus"), basestring):
  19. data["docstatus"] = json.loads(data["docstatus"])
  20. return data
  21. def execute(doctype, query=None, filters=None, fields=None, docstatus=None,
  22. group_by=None, order_by=None, limit_start=0, limit_page_length=None,
  23. as_list=False, with_childnames=False, debug=False):
  24. if query:
  25. return run_custom_query(query)
  26. if not filters: filters = []
  27. if not docstatus: docstatus = []
  28. args = prepare_args(doctype, filters, fields, docstatus, group_by, order_by, with_childnames)
  29. args.limit = add_limit(limit_start, limit_page_length)
  30. query = """select %(fields)s from %(tables)s where %(conditions)s
  31. %(group_by)s order by %(order_by)s %(limit)s""" % args
  32. return webnotes.conn.sql(query, as_dict=not as_list, debug=debug)
  33. def prepare_args(doctype, filters, fields, docstatus, group_by, order_by, with_childnames):
  34. webnotes.local.reportview_tables = get_tables(doctype, fields)
  35. load_doctypes()
  36. remove_user_tags(doctype, fields)
  37. conditions = build_conditions(doctype, fields, filters, docstatus)
  38. args = webnotes._dict()
  39. if with_childnames:
  40. for t in webnotes.local.reportview_tables:
  41. if t != "`tab" + doctype + "`":
  42. fields.append(t + ".name as '%s:name'" % t[4:-1])
  43. # query dict
  44. args.tables = ', '.join(webnotes.local.reportview_tables)
  45. args.conditions = ' and '.join(conditions)
  46. args.fields = ', '.join(fields)
  47. args.order_by = order_by or webnotes.local.reportview_tables[0] + '.modified desc'
  48. args.group_by = group_by and (" group by " + group_by) or ""
  49. check_sort_by_table(args.order_by)
  50. return args
  51. def compress(data):
  52. """separate keys and values"""
  53. if not data: return data
  54. values = []
  55. keys = data[0].keys()
  56. for row in data:
  57. new_row = []
  58. for key in keys:
  59. new_row.append(row[key])
  60. values.append(new_row)
  61. return {
  62. "keys": keys,
  63. "values": values
  64. }
  65. def check_sort_by_table(sort_by):
  66. """check atleast 1 column selected from the sort by table """
  67. if "." in sort_by:
  68. tbl = sort_by.split('.')[0]
  69. if tbl not in webnotes.local.reportview_tables:
  70. if tbl.startswith('`'):
  71. tbl = tbl[4:-1]
  72. webnotes.msgprint("Please select atleast 1 column from '%s' to sort"\
  73. % tbl, raise_exception=1)
  74. def run_custom_query(query):
  75. """run custom query"""
  76. if '%(key)s' in query:
  77. query = query.replace('%(key)s', 'name')
  78. return webnotes.conn.sql(query, as_dict=1)
  79. def load_doctypes():
  80. """load all doctypes and roles"""
  81. import webnotes.model.doctype
  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 webnotes.local.reportview_doctypes.get(doctype):
  88. continue
  89. if not webnotes.has_permission(doctype):
  90. raise webnotes.PermissionError, doctype
  91. webnotes.local.reportview_doctypes[doctype] = webnotes.model.doctype.get(doctype)
  92. def remove_user_tags(doctype, fields):
  93. """remove column _user_tags if not in table"""
  94. columns = get_table_columns(doctype)
  95. del_user_tags = False
  96. del_comments = False
  97. for fld in fields:
  98. if '_user_tags' in fld and not "_user_tags" in columns:
  99. del_user_tags = fld
  100. if '_comments' in fld and not "_comments" in columns:
  101. del_comments = fld
  102. if del_user_tags: del fields[fields.index(del_user_tags)]
  103. if del_comments: del fields[fields.index(del_comments)]
  104. def add_limit(limit_start, limit_page_length):
  105. if limit_page_length:
  106. return 'limit %s, %s' % (limit_start, limit_page_length)
  107. else:
  108. return ''
  109. def build_conditions(doctype, fields, filters, docstatus):
  110. """build conditions"""
  111. if docstatus:
  112. conditions = [webnotes.local.reportview_tables[0] + '.docstatus in (' + ','.join(docstatus) + ')']
  113. else:
  114. # default condition
  115. conditions = [webnotes.local.reportview_tables[0] + '.docstatus < 2']
  116. # make conditions from filters
  117. build_filter_conditions(filters, conditions)
  118. # join parent, child tables
  119. for tname in webnotes.local.reportview_tables[1:]:
  120. conditions.append(tname + '.parent = ' + webnotes.local.reportview_tables[0] + '.name')
  121. # match conditions
  122. match_conditions = build_match_conditions(doctype, fields)
  123. if match_conditions:
  124. conditions.append(match_conditions)
  125. return conditions
  126. def build_filter_conditions(filters, conditions):
  127. """build conditions from user filters"""
  128. from webnotes.utils import cstr, flt
  129. if not getattr(webnotes.local, "reportview_tables", None):
  130. webnotes.local.reportview_tables = []
  131. doclist = {}
  132. for f in filters:
  133. if isinstance(f, basestring):
  134. conditions.append(f)
  135. else:
  136. tname = ('`tab' + f[0] + '`')
  137. if not tname in webnotes.local.reportview_tables:
  138. webnotes.local.reportview_tables.append(tname)
  139. load_doctypes()
  140. # prepare in condition
  141. if f[2] in ['in', 'not in']:
  142. opts = ["'" + t.strip().replace("'", "\\'") + "'" for t in f[3].split(',')]
  143. f[3] = "(" + ', '.join(opts) + ")"
  144. conditions.append('ifnull(' + tname + '.' + f[1] + ", '') " + f[2] + " " + f[3])
  145. else:
  146. if isinstance(f[3], basestring):
  147. df = webnotes.local.reportview_doctypes[f[0]].get({"doctype": "DocField",
  148. "fieldname": f[1]})
  149. if df and df[0].fieldtype in ["Float", "Int", "Currency", "Percent"]:
  150. val, default_null_val = flt(f[3]), "0"
  151. else:
  152. val, default_null_val = ("'" + f[3].replace("'", "\\'") + "'"), '""'
  153. else:
  154. val, default_null_val = f[3], '0'
  155. conditions.append('ifnull({tname}.{fname}, {default_val}) {operator} {value}'.format(
  156. tname=tname, fname=f[1], default_val=default_null_val, operator=f[2],
  157. value=cstr(val)))
  158. def build_match_conditions(doctype, fields=None, as_condition=True):
  159. """add match conditions if applicable"""
  160. import webnotes.permissions
  161. match_filters = {}
  162. match_conditions = []
  163. if not getattr(webnotes.local, "reportview_tables", None):
  164. webnotes.local.reportview_tables = get_tables(doctype, fields)
  165. load_doctypes()
  166. # get restrictions
  167. restrictions = webnotes.defaults.get_restrictions()
  168. if restrictions:
  169. fields_to_check = webnotes.local.reportview_doctypes[doctype].get_restricted_fields(restrictions.keys())
  170. if doctype in restrictions:
  171. fields_to_check.append(webnotes._dict({"fieldname":"name", "options":doctype}))
  172. # check in links
  173. for df in fields_to_check:
  174. if as_condition:
  175. match_conditions.append('`tab{doctype}`.{fieldname} in ({values})'.format(doctype=doctype,
  176. fieldname=df.fieldname,
  177. values=", ".join([('"'+v.replace('"', '\"')+'"') for v in restrictions[df.options]])))
  178. else:
  179. match_filters.setdefault(df.fieldname, [])
  180. match_filters[df.fieldname]= restrictions[df.options]
  181. # add owner match
  182. owner_match = True
  183. for p in webnotes.permissions.get_user_perms(webnotes.local.reportview_doctypes[doctype], "read"):
  184. if not (p.match and p.match=="owner"):
  185. owner_match = False
  186. break
  187. if owner_match:
  188. match_conditions.append('`tab{doctype}`.`owner`="{user}"'.format(doctype=doctype,
  189. user=webnotes.local.session.user))
  190. match_filters["owner"] = [webnotes.local.session.user]
  191. if as_condition:
  192. conditions = " and ".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. form_params = get_form_params()
  241. webnotes.permissions.can_export(form_params.doctype, raise_exception=True)
  242. ret = execute(**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 get_labels(columns):
  266. """get column labels based on column names"""
  267. label_dict = {}
  268. for doctype in webnotes.local.reportview_doctypes:
  269. for d in webnotes.local.reportview_doctypes[doctype]:
  270. if d.doctype=='DocField' and d.fieldname:
  271. label_dict[d.fieldname] = d.label
  272. return map(lambda x: label_dict.get(x, x.title()), columns)
  273. @webnotes.whitelist()
  274. def delete_items():
  275. """delete selected items"""
  276. import json
  277. from webnotes.model.code import get_obj
  278. il = json.loads(webnotes.form_dict.get('items'))
  279. doctype = webnotes.form_dict.get('doctype')
  280. for d in il:
  281. try:
  282. dt_obj = get_obj(doctype, d)
  283. if hasattr(dt_obj, 'on_trash'):
  284. dt_obj.on_trash()
  285. webnotes.delete_doc(doctype, d)
  286. except Exception, e:
  287. webnotes.errprint(webnotes.get_traceback())
  288. pass
  289. @webnotes.whitelist()
  290. def get_stats(stats, doctype):
  291. """get tag info"""
  292. import json
  293. tags = json.loads(stats)
  294. stats = {}
  295. columns = get_table_columns(doctype)
  296. for tag in tags:
  297. if not tag in columns: continue
  298. tagcount = execute(doctype, fields=[tag, "count(*)"],
  299. filters=["ifnull(%s,'')!=''" % tag], group_by=tag, as_list=True)
  300. if tag=='_user_tags':
  301. stats[tag] = scrub_user_tags(tagcount)
  302. else:
  303. stats[tag] = tagcount
  304. return stats
  305. def scrub_user_tags(tagcount):
  306. """rebuild tag list for tags"""
  307. rdict = {}
  308. tagdict = dict(tagcount)
  309. for t in tagdict:
  310. alltags = t.split(',')
  311. for tag in alltags:
  312. if tag:
  313. if not tag in rdict:
  314. rdict[tag] = 0
  315. rdict[tag] += tagdict[t]
  316. rlist = []
  317. for tag in rdict:
  318. rlist.append([tag, rdict[tag]])
  319. return rlist
  320. def get_table_columns(table):
  321. res = webnotes.conn.sql("DESC `tab%s`" % table, as_dict=1)
  322. if res: return [r['Field'] for r in res]
  323. # used in building query in queries.py
  324. def get_match_cond(doctype, searchfield = 'name'):
  325. cond = build_match_conditions(doctype)
  326. if cond:
  327. cond = ' and ' + cond
  328. else:
  329. cond = ''
  330. return cond