Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
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')
Exemplo n.º 6
0
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,
    )