def search_results(): form = SearchForm(request.args) parser = FlaskParser() search_kwargs = parser.parse(search_args, request) if search_kwargs.get('page_size') is None or search_kwargs.get('page_size') == '': search_kwargs['page_size'] = '25' if search_kwargs.get('page') is None or search_kwargs.get('page') == '': search_kwargs['page'] = '1' if (search_kwargs.get('page_number') is None or search_kwargs.get('page_number') == '') \ and search_kwargs.get('page') is not None: search_kwargs['page_number'] = search_kwargs['page'] sp = SearchPublications(search_url) search_results_response, resp_status_code = sp.get_pubs_search_results( params=search_kwargs) # go out to the pubs API and get the search results try: search_result_records = search_results_response['records'] record_count = search_results_response['recordCount'] pagination = Pagination(page=int(search_kwargs['page_number']), total=record_count, per_page=int(search_kwargs['page_size']), record_name='Search Results', bs_version=3) search_service_down = None start_plus_size = int(search_results_response['pageRowStart']) + int(search_results_response['pageSize']) if record_count < start_plus_size: record_max = record_count else: record_max = start_plus_size result_summary = {'record_count': record_count, 'page_number': search_results_response['pageNumber'], 'records_per_page': search_results_response['pageSize'], 'record_min': (int(search_results_response['pageRowStart']) + 1), 'record_max': record_max} except TypeError: search_result_records = None pagination = None search_service_down = 'The backend services appear to be down with a {0} status.'.format(resp_status_code) result_summary = {} if 'mimetype' in request.args and request.args.get("mimetype") == 'ris': content = render_template('pubswh/ris_output.ris', search_result_records=search_result_records) return Response(content, mimetype="application/x-research-info-systems", headers={"Content-Disposition":"attachment;filename=PubsWarehouseResults.ris"}) if request.args.get('map') == 'True': for record in search_result_records: record = jsonify_geojson(record) return render_template('pubswh/search_results.html', result_summary=result_summary, search_result_records=search_result_records, pagination=pagination, search_service_down=search_service_down, form=form, pub_url=pub_url)
def search_results(): parser = FlaskParser() search_kwargs = parser.parse(search_args, request) form = SearchForm(None, obj=request.args, ) # populate form based on parameter form.advanced.data = True form_element_list = ['q', 'title', 'contributingOffice', 'contributor', 'typeName', 'subtypeName', 'seriesName', 'reportNumber', 'year'] for element in form_element_list: if len(search_kwargs[element]) > 0: form[element].data = search_kwargs[element][0] if search_kwargs.get('page_size') is None or search_kwargs.get('page_size') == '': search_kwargs['page_size'] = '25' if search_kwargs.get('page') is None or search_kwargs.get('page') == '': search_kwargs['page'] = '1' if (search_kwargs.get('page_number') is None or search_kwargs.get('page_number') == '') \ and search_kwargs.get('page') is not None: search_kwargs['page_number'] = search_kwargs['page'] sp = SearchPublications(search_url) search_results_response, resp_status_code = sp.get_pubs_search_results( params=search_kwargs) # go out to the pubs API and get the search results try: search_result_records = search_results_response['records'] record_count = search_results_response['recordCount'] pagination = Pagination(page=int(search_kwargs['page_number']), total=record_count, per_page=int(search_kwargs['page_size']), record_name='Search Results', bs_version=3) search_service_down = None start_plus_size = int(search_results_response['pageRowStart']) + int(search_results_response['pageSize']) if record_count < start_plus_size: record_max = record_count else: record_max = start_plus_size result_summary = {'record_count': record_count, 'page_number': search_results_response['pageNumber'], 'records_per_page': search_results_response['pageSize'], 'record_min': (int(search_results_response['pageRowStart']) + 1), 'record_max': record_max} except TypeError: search_result_records = None pagination = None search_service_down = 'The backend services appear to be down with a {0} status.'.format(resp_status_code) result_summary = {} return render_template('search_results.html', advanced=search_kwargs['advanced'], result_summary=result_summary, search_result_records=search_result_records, pagination=pagination, search_service_down=search_service_down, form=form)
def _get_args(cls, **kwargs): """Parse style and locale. Argument location precedence: kwargs > view_args > query """ csl_args = {'style': cls._default_style, 'locale': cls._default_locale} if has_request_context(): parser = FlaskParser(locations=('view_args', 'query')) csl_args.update(parser.parse(cls._user_args, request)) csl_args.update( {k: kwargs[k] for k in ('style', 'locale') if k in kwargs}) try: csl_args['style'] = get_style_filepath(csl_args['style'].lower()) except StyleNotFoundError: if has_request_context(): raise StyleNotFoundRESTError(csl_args['style']) raise return csl_args
def _get_args(cls, **kwargs): """Parse style and locale. Argument location precedence: kwargs > view_args > query """ csl_args = { 'style': cls._default_style, 'locale': cls._default_locale } if has_request_context(): parser = FlaskParser(locations=('view_args', 'query')) csl_args.update(parser.parse(cls._user_args, request)) csl_args.update({k: kwargs[k] for k in ('style', 'locale') if k in kwargs}) try: csl_args['style'] = get_style_filepath(csl_args['style'].lower()) except StyleNotFoundError: if has_request_context(): raise StyleNotFoundRESTError(csl_args['style']) raise return csl_args
class BaseController: REQUIRED_ENV = None AUTH_BACKEND = None def __init__(self, import_name, name, prefix, template_folder=None): self.blueprint = Blueprint(import_name=import_name, name=name, url_prefix=prefix, template_folder=template_folder) self.prefix = prefix self.name = name self.parser = FlaskParser() self.apidoc = {} @self.parser.error_handler def _handle_error(err): raise err def bind(self, core): """ 将该controller绑定到app上,并注册相关事件 """ app = core.app if self.REQUIRED_ENV: env = app.config['ENVIRONMENT'] if env != self.REQUIRED_ENV: return False app.register_blueprint(self.blueprint) return True def _using_apidoc(self, fn): """ 判断当前视图函数是否开启apidoc """ name = getattr(fn, '__name__', None) return name in self.apidoc def _route(self, route, method, **options): """ 绑定视图函数到blueprint上 """ def decorator(fn): # 检查apidoc if self._using_apidoc(fn): doc = self.apidoc[fn.__name__] doc['method'] = method doc['route'] = self.prefix + route options['methods'] = [method] return self.blueprint.route(route, **options)(fn) return decorator def get(self, route, **options): return self._route(route, 'GET', **options) def post(self, route, **options): return self._route(route, 'POST', **options) def login_required(self): """ 校验存在已验证用户 如果检查到user为BannedUser的实例时,意味着用户被禁止访问 """ def decorator(fn): @wraps(fn) def wrap(*args, **kwargs): if not current_user: return self.handle_unauthorization() if isinstance(self.AUTH_BACKEND, str): backend = self.AUTH_BACKEND elif isinstance(self.AUTH_BACKEND, type) and issubclass( self.AUTH_BACKEND, AbstractBackend): backend = self.AUTH_BACKEND.NAME else: backend = str(self.AUTH_BACKEND) if getattr(g, 'backend', None) and self.AUTH_BACKEND: if g.backend != backend: return self.handle_unauthorization() if isinstance(current_user, BannedUser): return self.handle_banned_user() return fn(*args, **kwargs) return wrap return decorator def visit_limit(self, seconds): """ 访问限制 根据通过验证的user id作为key一部分,所以要使用在login_required之后 """ def decorator(fn): @wraps(fn) def wrap(*args, **kwargs): if not current_user: return self.handle_unauthorization() cache = current_app.core.cache user_id = current_user.id fn_key = f'{self.name}_{fn.__name__}' key = CacheKey('visit_limit', user_id, fn_key) if cache.nget(key): if cache.ttl(key) == -1: cache.expire(key, seconds) return self.handle_visit_limit() rst = cache.nincr(key) cache.expire(key, seconds) if rst > 1: return self.handle_visit_limit() return fn(*args, **kwargs) return wrap return decorator def use_args(self, fields, code, validate=None, location='form'): """ 校验请求参数 :param fields: 检验字段 :param code: 检验错误时错误码 :param validate: 对整个表单的校验 :param location: 检验参数所在位置(form, query, json, headers...) :return: """ assert location in ['query', 'form'], \ 'location should use "query" or "form"' def decorator(fn): # 检查apidoc if self._using_apidoc(fn): _args = self.apidoc[fn.__name__].get('args', {}) _location = _args.get(location, {}) _location.update(fields) _args[location] = _location self.apidoc[fn.__name__]['args'] = _args self._add_error_info(code, fn.__name__) @wraps(fn) def wrap(*args, **kwargs): try: result = self.parser.parse(fields, validate=validate, locations=('querystring', 'form', 'json')) if 'parsed' in kwargs: old = kwargs.pop('parsed') old.update(result) result = old return fn(parsed=result, *args, **kwargs) except ValidationError as err: return self.handle_bad_arguments(err.messages) return wrap return decorator def add_apidoc(self, errors=None, response=None): """ 开启api文档 :param errors: controller自身会自动添加一部分信息于errors中 :param response: 请求成功后返回的格式 """ def decorator(fn): response_doc = response or {} response_doc['code'] = 0 api_name = fn.__name__ self.apidoc[api_name] = { 'doc': fn.__doc__ or '', 'errors': errors or dict(), 'response': json.dumps(response_doc, indent=2, sort_keys=True, default=lambda o: o.__dict__) } return fn return decorator def format_doc(self): """ 格式化文档数据 """ _formatted = {} for view_name, doc_map in self.apidoc.items(): # 处理args args = {} temp = dict(**doc_map) if 'args' in doc_map: for location, fields in doc_map['args'].items(): args[location] = {} for arg_name, arg_field in fields.items(): args[location][arg_name] = { 'type': arg_field.__class__.__name__, 'required': getattr(arg_field, 'required', False) } temp['args'] = args # 处理注释 doc_text = doc_map.get('doc', '').split('\n') temp['doc'] = list( filter(lambda t: len(t), [t.strip() for t in doc_text])) _formatted[view_name] = temp return _formatted def _add_error_info(self, error_code, api_name): """ 自动添加部分错误信息 """ errors = self.apidoc[api_name]['errors'] if error_code not in errors: errors[error_code] = self.handle_get_error_text(error_code) # custom handler def handle_unauthorization(self, msg=None): return abort(401) def handle_bad_arguments(self, msg=None): return abort(422) def handle_visit_limit(self, msg=None): return abort(403) def handle_banned_user(self, msg=None): return abort(403) def handle_get_error_text(self, code): return STATUS_TEXT.get(code, 'error')
def search_results(): form = SearchForm(request.args) parser = FlaskParser() search_kwargs = parser.parse(search_args, request) if search_kwargs.get("page_size") is None or search_kwargs.get("page_size") == "": search_kwargs["page_size"] = "25" if search_kwargs.get("page") is None or search_kwargs.get("page") == "": search_kwargs["page"] = "1" if (search_kwargs.get("page_number") is None or search_kwargs.get("page_number") == "") and search_kwargs.get( "page" ) is not None: search_kwargs["page_number"] = search_kwargs["page"] sp = SearchPublications(search_url) search_results_response, resp_status_code = sp.get_pubs_search_results( params=search_kwargs ) # go out to the pubs API and get the search results try: search_result_records = search_results_response["records"] record_count = search_results_response["recordCount"] pagination = Pagination( page=int(search_kwargs["page_number"]), total=record_count, per_page=int(search_kwargs["page_size"]), record_name="Search Results", bs_version=3, ) search_service_down = None start_plus_size = int(search_results_response["pageRowStart"]) + int(search_results_response["pageSize"]) if record_count < start_plus_size: record_max = record_count else: record_max = start_plus_size result_summary = { "record_count": record_count, "page_number": search_results_response["pageNumber"], "records_per_page": search_results_response["pageSize"], "record_min": (int(search_results_response["pageRowStart"]) + 1), "record_max": record_max, } except TypeError: search_result_records = None pagination = None search_service_down = "The backend services appear to be down with a {0} status.".format(resp_status_code) result_summary = {} if "mimetype" in request.args and request.args.get("mimetype") == "ris": content = render_template("pubswh/ris_output.ris", search_result_records=search_result_records) return Response( content, mimetype="application/x-research-info-systems", headers={"Content-Disposition": "attachment;filename=PubsWarehouseResults.ris"}, ) if request.args.get("map") == "True": for record in search_result_records: record = jsonify_geojson(record) return render_template( "pubswh/search_results.html", result_summary=result_summary, search_result_records=search_result_records, pagination=pagination, search_service_down=search_service_down, form=form, pub_url=pub_url, )