# -*- coding: utf-8 -*- import json from odoo import http from odoo.http import request from odoo.utils import util from .base import BaseController import logging _logger = logging.getLogger(__name__) import _ast import re import csv try: # Python 3 import configparser from threading import current_thread from xmlrpc.client import Fault, ServerProxy, MININT, MAXINT PY2 = False except ImportError: # Python 2 import ConfigParser as configparser from threading import currentThread as current_thread from xmlrpclib import Fault, ServerProxy, MININT, MAXINT PY2 = True DOMAIN_OPERATORS = frozenset('!|&') _term_re = re.compile( '([\w._]+)\s*' '(=(?:like|ilike|\?)|[<>]=?|!?=(?!=)' '|(?<= )(?:like|ilike|in|not like|not ilike|not in|child_of))' '\s*(.*)') # Simplified ast.literal_eval which does not parse operators def _convert(node, _consts={'None': None, 'True': True, 'False': False}): if isinstance(node, _ast.Str): return node.s if isinstance(node, _ast.Num): return node.n if isinstance(node, _ast.Tuple): return tuple(map(_convert, node.elts)) if isinstance(node, _ast.List): return list(map(_convert, node.elts)) if isinstance(node, _ast.Dict): return dict([(_convert(k), _convert(v)) for (k, v) in zip(node.keys, node.values)]) if hasattr(node, 'value') and str(node.value) in _consts: return node.value # Python 3.4+ if isinstance(node, _ast.Name) and node.id in _consts: return _consts[node.id] # Python <= 3.3 raise ValueError('malformed or disallowed expression') if PY2: int_types = int, long class _DictWriter(csv.DictWriter): """Unicode CSV Writer, which encodes output to UTF-8.""" def writeheader(self): # Method 'writeheader' does not exist in Python 2.6 header = dict(zip(self.fieldnames, self.fieldnames)) self.writerow(header) def _dict_to_list(self, rowdict): rowlst = csv.DictWriter._dict_to_list(self, rowdict) return [cell.encode('utf-8') if hasattr(cell, 'encode') else cell for cell in rowlst] else: # Python 3 basestring = str int_types = int _DictWriter = csv.DictWriter def literal_eval(expression, _octal_digits=frozenset('01234567')): node = compile(expression, '', 'eval', _ast.PyCF_ONLY_AST) if expression[:1] == '0' and expression[1:2] in _octal_digits: raise SyntaxError('unsupported octal notation') value = _convert(node.body) if isinstance(value, int_types) and not MININT <= value <= MAXINT: raise ValueError('overflow, int exceeds XML-RPC limits') return value def searchargs(params, kwargs=None, context=None): """Compute the 'search' parameters.""" if not params: return [] domain = params[0] if not isinstance(domain, list): return params for (idx, term) in enumerate(domain): if isinstance(term, basestring) and term not in DOMAIN_OPERATORS: m = _term_re.match(term.strip()) if not m: raise ValueError('Cannot parse term %r' % term) (field, operator, value) = m.groups() try: value = literal_eval(value) except Exception: # Interpret the value as a string pass domain[idx] = (field, operator, value) return domain class Dropdown(http.Controller, BaseController): @http.route('//drop-down', auth='public', methods=['POST'], csrf=False) def drop_down(self, sub_domain, **kwargs): # 需要传递过来参数token param_list = self._get_params( ['token', 'model', 'count_per_page', 'page_index', 'search_field', 'search_value', 'order', 'filter', 'data_type', 'id_field', 'name_field']) # data_type:用于数据授权:当同一个档案要使用不同数据授权时,通过该字段区分 if not param_list: return self.res_err(-1, u'未处理的请求类型,请联系开发者') user_token, model, count_per_page, page_index, search_field, search_value, order, _filter, data_type, id_field, name_field = param_list if not user_token: return self.res_err(601, u'无权操作:缺少 token') if not model: return self.res_err(600, u'缺少参数:缺少 要查询的档案model') if not id_field: id_field = 'id' else: search_field = id_field wx_user, info = self._query_wx_user(user_token) request.uid = wx_user.user_id.id if info: return info return self._query_dropdown_data(wx_user, model, count_per_page, page_index, search_field, search_value, order, _filter, data_type, id_field, name_field) def _get_params(self, param_list): if not param_list: return None param_values = [] try: encrypt_type = request.params.get('encrypt_type', 'raw') if encrypt_type == 'raw': if request.httprequest.data: dic = json.loads(request.httprequest.data) for p in param_list: param_values.append(dic.get(p, None)) else: for p in param_list: v = request.httprequest.values[p] if p in request.httprequest.values else None param_values.append(v) return param_values else: _logger.info(u'>>> ===error===未处理的请求类型') return None except Exception as e: _logger.info(u'>>> ===error===获取参数出错:' + str(e)) return None def _query_wx_user(self, user_token): # 已保证微信用户与系统用户关联起来 access_token = request.env(user=1)['wxapp.access_token'].search([ ('token', '=', user_token), ]) if not access_token: return None, self.res_err(10000, u'微信用户未注册,或登录信息过期') if not access_token.open_id: return None, self.res_err(9999, u'用户未登录') user = request.env(user=1)['wxapp.user'].search([ ('open_id', '=', access_token.open_id), ]) _logger.info(u'>>> open_id======' + access_token.open_id) _logger.info(u'>>> wx_user======' + user.name if user else u'无') if not user or not user.user_id.id or not user.user_id.active: return None, self.res_err(9999, u'请等待系统管理员授权: 当前微信用户,没有与系统用户关联') return user, None def _query_dropdown_data(self, wx_user, model, count_per_page, page_index, search_field, search_value, order, _filter, data_type, id_field, name_field): count_per_page = self._get_valid_count_per_page(count_per_page) page_index = self._get_valid_page_index(page_index) domain = self._get_domain(search_field, search_value) # self._add_organization(domain, model, data_type) # print('filter:', _filter) _filter = Dropdown._get_valid_filter(_filter) # print('filter after deal:', _filter) if _filter: domain += _filter # print '===>domain', domain, ',order:', order, ',limit:', count_per_page if order: data = request.env(user=wx_user.user_id.id)[model].search(domain, limit=count_per_page, offset=count_per_page * page_index, order=order) else: data = request.env(user=wx_user.user_id.id)[model].search(domain, limit=count_per_page, offset=count_per_page * page_index) if id_field == 'id': _name_list = [{'id': row.id, 'name': row[name_field] if name_field else row.name} for row in data] if data else [] else: _name_list = [{'id': row[id_field].id, 'name': row[name_field] if name_field else row[id_field].name} for row in data if row[id_field]] if data else [] data = { "_list": _name_list } # print 'data:', data # print 'result:', _name_list return self.res_ok(data) @staticmethod def _get_valid_filter(_filter): if not _filter: return None _filter = eval(_filter) if util.is_string(_filter) else _filter _filter = searchargs(_filter) if _filter and util.is_list(_filter): _need_replace = [] for _tuple in _filter: if len(_tuple) > 2: if util.is_string(_tuple[2]): new_tuple = (_tuple[0], _tuple[1], _tuple[2]) _need_replace.append((_tuple, new_tuple)) else: new_tuple = (_tuple[0], _tuple[1], None) _need_replace.append((_tuple, new_tuple)) if _need_replace: for old, _new in _need_replace: index = _filter.index(old) _filter.remove(old) _filter.insert(index, _new) return _filter return None def _get_valid_count_per_page(self, count_per_page): if not count_per_page: return 80 try: count_per_page = int(count_per_page) except Exception: return 80 if count_per_page < 10: count_per_page = 80 return count_per_page def _get_valid_page_index(self, page_index): if not page_index: return 0 try: page_index = int(page_index) except Exception: return 0 if page_index < 0: page_index = 0 return page_index def _get_domain(self, search_field, search_value): if not search_field or not search_value: return [] return [(search_field, 'ilike', search_value)] @http.route('//drop-down-fault', auth='public', methods=['POST'], csrf=False) def query_fault(self, sub_domain, **kwargs): # 需要传递过来参数token param_list = self._get_params( ['token', 'model', 'count_per_page', 'page_index', 'search_field', 'search_value', 'order', 'filter', 'data_type', 'id_field', 'name_field']) # data_type:用于数据授权:当同一个档案要使用不同数据授权时,通过该字段区分 if not param_list: return self.res_err(-1, u'未处理的请求类型,请联系开发者') # print('all param list:', param_list) user_token, model, count_per_page, page_index, search_field, search_value, order, _filter, data_type, id_field, name_field = param_list if not user_token: return self.res_err(601, u'无权操作:缺少 token') if not id_field: id_field = 'id' else: search_field = id_field wx_user, info = self._query_wx_user(user_token) request.uid = wx_user.user_id.id if info: return info return self._query_dropdown_data_fault(wx_user, count_per_page, page_index, search_field, search_value, _filter) def _query_dropdown_data_fault(self, wx_user, count_per_page, page_index, search_field, search_value, _filter): count_per_page = self._get_valid_count_per_page(count_per_page) page_index = self._get_valid_page_index(page_index) domain = self._get_domain(search_field, search_value) # self._add_organization(domain, model, data_type) _filter = Dropdown._get_valid_filter(_filter) has_valid_filter = _filter[0][2] if _filter else False where_exp, where_param = self._get_where_from(domain, _filter, has_valid_filter) limit_exp = count_per_page offset_exp = count_per_page * page_index where_param.append(limit_exp) where_param.append(offset_exp) if has_valid_filter: sql_format = "select f.id,f.name from archives_fault f " \ "left join archives_equipment_fault_rel r on r.fault_id=f.id " \ "{} limit %s offset %s" sql = sql_format.format(where_exp) else: sql_format = "select f.id,f.name from archives_fault f " \ "{} limit %s offset %s" sql = sql_format.format(where_exp) # print('sql:', sql) # print('params:', where_param) cr = request.env(user=wx_user.user_id.id).cr cr.execute(sql, where_param) data = cr.fetchall() _name_list = [{'id': row[0], 'name': row[1]} for row in data] if data else [] data = { "_list": _name_list } return self.res_ok(data) def _get_where_from(self, domain, _filter, has_valid_filter): _param_value_list = [] if domain: _and_list_domain = [] for field, operator, value in domain: self._add_condition_and_param(_and_list_domain, _param_value_list, field, operator, value) domain_condition = ' and '.join(_and_list_domain) else: domain_condition = '' if has_valid_filter: _filter_condition_list = [] for field, operator, value in _filter: self._add_condition_and_param(_filter_condition_list, _param_value_list, field, operator, value) _filter_condition = ' and '.join(_filter_condition_list) _format = '(({}) or f.is_common=true)' if len(_filter_condition_list) > 1 else '({} or f.is_common=true)' _filter_condition = _format.format(_filter_condition) else: _filter_condition = '' if domain and has_valid_filter: condition = '{} and {}'.format(domain_condition, _filter_condition) else: condition = domain_condition or _filter_condition if condition: condition = 'where {}'.format(condition) return condition, _param_value_list @staticmethod def _add_condition_and_param(condition_list, param_ist, field, operator, value): # print('field:', field) # print('operator:', operator) # print('value:', value) has_prefix = '.' in field add_prefix = '' if has_prefix else 'f.' if operator == 'ilike': condition_list.append('{}{} ilike %s'.format(add_prefix, field)) param_ist.append('%{}%'.format(value)) return condition_list.append("{}{} {} %s".format(add_prefix, field, operator)) param_ist.append(value) return @http.route('//drop-down-fault-reason', auth='public', methods=['POST'], csrf=False) def query_fault_reason(self, sub_domain, **kwargs): # 需要传递过来参数token param_list = self._get_params( ['token', 'model', 'count_per_page', 'page_index', 'search_field', 'search_value', 'order', 'filter', 'data_type', 'id_field', 'name_field']) # data_type:用于数据授权:当同一个档案要使用不同数据授权时,通过该字段区分 if not param_list: return self.res_err(-1, u'未处理的请求类型,请联系开发者') # print('all param list:', param_list) user_token, model, count_per_page, page_index, search_field, search_value, order, _filter, data_type, id_field, name_field = param_list if not user_token: return self.res_err(601, u'无权操作:缺少 token') if not id_field: id_field = 'id' else: search_field = id_field wx_user, info = self._query_wx_user(user_token) request.uid = wx_user.user_id.id if info: return info return self._query_dropdown_data_fault_reason(wx_user, count_per_page, page_index, search_field, search_value, _filter, order) def _query_dropdown_data_fault_reason(self, wx_user, count_per_page, page_index, search_field, search_value, _filter, order): count_per_page = self._get_valid_count_per_page(count_per_page) page_index = self._get_valid_page_index(page_index) domain = self._get_domain(search_field, search_value) # self._add_organization(domain, model, data_type) _filter = Dropdown._get_valid_filter(_filter) has_valid_filter = _filter[0][2] if _filter else False where_exp, where_param = self._get_fault_reason_where_from(domain, _filter, has_valid_filter) limit_exp = count_per_page offset_exp = count_per_page * page_index where_param.append(limit_exp) where_param.append(offset_exp) if order: order = 'order by {}'.format(order) sql_format = "select id,name from archives_fault_reason " \ "{} {} limit %s offset %s" sql = sql_format.format(where_exp, order) # print('sql:', sql) # print('params:', where_param) cr = request.env(user=wx_user.user_id.id).cr cr.execute(sql, where_param) data = cr.fetchall() _name_list = [{'id': row[0], 'name': row[1]} for row in data] if data else [] data = { "_list": _name_list } return self.res_ok(data) def _get_fault_reason_where_from(self, domain, _filter, has_valid_filter): _param_value_list = [] if domain: _and_list_domain = [] for field, operator, value in domain: self._add_condition_and_param_4_fault_reason(_and_list_domain, _param_value_list, field, operator, value) domain_condition = ' and '.join(_and_list_domain) else: domain_condition = '' if has_valid_filter: _filter_condition_list = [] for field, operator, value in _filter: self._add_condition_and_param_4_fault_reason(_filter_condition_list, _param_value_list, field, operator, value) _filter_condition = ' and '.join(_filter_condition_list) _format = '(({}) or fault_id is null)' if len(_filter_condition_list) > 1 else '({} or fault_id is null)' _filter_condition = _format.format(_filter_condition) else: _filter_condition = '' if domain and has_valid_filter: condition = '{} and {}'.format(domain_condition, _filter_condition) else: condition = domain_condition or _filter_condition if condition: condition = 'where {}'.format(condition) return condition, _param_value_list @staticmethod def _add_condition_and_param_4_fault_reason(condition_list, param_ist, field, operator, value): # print('field:', field) # print('operator:', operator) # print('value:', value) condition_list.append("{} {} %s".format(field, operator)) if operator == 'ilike': value = '%{}%'.format(value) param_ist.append(value) return