25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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