Ejemplo n.º 1
0
def test_api(app, db):
    taxonomy = current_flask_taxonomies.create_taxonomy("root",
                                                        extra_data={},
                                                        session=db.session)
    db.session.commit()

    identification = TermIdentification(taxonomy=taxonomy, slug="a")
    term = current_flask_taxonomies.create_term(identification,
                                                extra_data={"a": 1})
    assert term.slug == "a"
    db.session.commit()

    identification2 = TermIdentification(parent=term, slug="b")
    term2 = current_flask_taxonomies.create_term(identification2,
                                                 extra_data={"b": 2})
    assert term2.slug == "a/b"
    db.session.commit()

    res = current_flask_taxonomies.list_taxonomies().all()
    print(res)

    # filter
    term_identification = TermIdentification(taxonomy=taxonomy, slug=term.slug)
    assert list(
        current_flask_taxonomies.filter_term(term_identification)) == [term]

    assert list(
        current_flask_taxonomies.filter_term(
            TermIdentification(taxonomy=taxonomy,
                               slug=term2.slug))) == [term2]

    res_term = current_flask_taxonomies.filter_term(
        term_identification).one_or_none()
    assert isinstance(res_term, TaxonomyTerm)
Ejemplo n.º 2
0
def test_lock_unlock_term(app, db, taxonomy_tree):
    term_identification = TermIdentification(taxonomy="test_taxonomy", slug="a/b/c")
    term = list(current_flask_taxonomies.filter_term(
        term_identification))[0]
    lock_term(locked_terms=[term.id], term=term)
    db.session.commit()
    term = list(current_flask_taxonomies.filter_term(
        term_identification))[0]
    assert term.busy_count == 1
    unlock_term(url=term.links().envelope["self"])
    term = list(current_flask_taxonomies.filter_term(
        term_identification))[0]
    assert term.busy_count == 0
Ejemplo n.º 3
0
def delete_taxonomy_term(code=None,
                         slug=None,
                         prefer=None,
                         page=None,
                         size=None,
                         q=None):
    if q:
        json_abort(
            422, {
                'message': 'Query not appropriate when deleting term',
                'reason': 'search-query-not-allowed'
            })
    try:
        taxonomy = current_flask_taxonomies.get_taxonomy(code)
        ti = TermIdentification(taxonomy=code, slug=slug)
        term = current_flask_taxonomies.filter_term(ti).one()

        current_flask_taxonomies.permissions.taxonomy_term_delete.enforce(
            request=request, taxonomy=taxonomy, term=term)
        term = current_flask_taxonomies.delete_term(TermIdentification(
            taxonomy=code, slug=slug),
                                                    remove_after_delete=False)
        current_flask_taxonomies.commit()

    except TaxonomyTermBusyError as e:
        return json_abort(412, {'message': str(e), 'reason': 'term-busy'})
    except NoResultFound as e:
        return json_abort(404, {})
    return jsonify(term.json(representation=prefer))
Ejemplo n.º 4
0
def create_update_terms(taxonomy,
                        taxonomy_data,
                        str_args: tuple = tuple(),
                        int_conversions: tuple = tuple(),
                        bool_args: tuple = tuple()):
    stack = [taxonomy]
    for term_dict in convert_data_to_dict(taxonomy_data, str_args=str_args,
                                          int_conversions=int_conversions, bool_args=bool_args):
        level = int(term_dict.pop('level'))
        try:
            slug = term_dict.pop('slug')
        except KeyError:
            slug = None
        while level < len(stack):
            stack.pop()
        if not slug:
            slug = slugify(term_dict["title"]["cs"])
        else:
            slug = slugify(slug)
        last_stack = stack[-1]
        if isinstance(last_stack, Taxonomy):
            identification = TermIdentification(taxonomy=taxonomy, slug=slug)
        else:
            identification = TermIdentification(parent=last_stack, slug=slug)
        term = current_flask_taxonomies.filter_term(identification).one_or_none()
        if term:
            current_flask_taxonomies.update_term(identification, extra_data=term_dict)
        else:
            term = current_flask_taxonomies.create_term(identification, extra_data=term_dict)
        stack.append(term)
    db.session.commit()
Ejemplo n.º 5
0
def get_term_by_link(link):
    slug, taxonomy_code = get_slug_from_link(link)
    term = current_flask_taxonomies.filter_term(
        TermIdentification(taxonomy=taxonomy_code, slug=slug)).one_or_none()
    if not term:
        return None
    return term
Ejemplo n.º 6
0
def _unlock_term(term_url):
    slug, taxonomy_code = get_slug_from_link(term_url)
    term_identification = TermIdentification(taxonomy=taxonomy_code, slug=slug)
    term_list = list(current_flask_taxonomies.filter_term(term_identification))
    if not term_list:
        return
    term = term_list[0]
    busy_count_0 = term.busy_count
    current_flask_taxonomies.unmark_busy([term.id])
    assert term.busy_count < busy_count_0
Ejemplo n.º 7
0
def get_taxonomy_json(code=None,
                      slug=None,
                      prefer: Representation = Representation("taxonomy"),
                      page=None,
                      size=None,
                      status_code=200,
                      q=None,
                      request=None):
    taxonomy = current_flask_taxonomies.get_taxonomy(code)
    prefer = taxonomy.merge_select(prefer)

    if request:
        current_flask_taxonomies.permissions.taxonomy_term_read.enforce(request=request,
                                                                        taxonomy=taxonomy,
                                                                        slug=slug)

    if INCLUDE_DELETED in prefer:
        status_cond = sqlalchemy.sql.true()
    else:
        status_cond = TaxonomyTerm.status == TermStatusEnum.alive

    return_descendants = INCLUDE_DESCENDANTS in prefer

    if return_descendants:
        query = current_flask_taxonomies.descendants_or_self(
            TermIdentification(taxonomy=code, slug=slug),
            levels=prefer.options.get('levels', None),
            status_cond=status_cond,
            return_descendants_count=INCLUDE_DESCENDANTS_COUNT in prefer,
            return_descendants_busy_count=INCLUDE_STATUS in prefer
        )
    else:
        query = current_flask_taxonomies.filter_term(
            TermIdentification(taxonomy=code, slug=slug),
            status_cond=status_cond,
            return_descendants_count=INCLUDE_DESCENDANTS_COUNT in prefer,
            return_descendants_busy_count=INCLUDE_STATUS in prefer
        )
    if q:
        query = current_flask_taxonomies.apply_term_query(query, q, code)
    paginator = Paginator(
        prefer,
        query, page if return_descendants else None,
        size if return_descendants else None,
        json_converter=lambda data:
        build_descendants(data, prefer, root_slug=None),
        allow_empty=INCLUDE_SELF not in prefer, single_result=INCLUDE_SELF in prefer,
        has_query=q is not None
    )
    return paginator
Ejemplo n.º 8
0
def test_create_update_terms_2(app, db, taxonomy_data_list):
    taxonomy_code = "licenses"
    slug = "copyright"
    taxonomy = current_flask_taxonomies.create_taxonomy(
        taxonomy_code,
        extra_data={"title": {
            "cs": "Licence",
            "en": "Licenses"
        }})
    create_update_terms(taxonomy, taxonomy_data_list,
                        resolve_list=True)  # creating
    create_update_terms(taxonomy, taxonomy_data_list,
                        resolve_list=True)  # updating
    term_identification = TermIdentification(taxonomy=taxonomy_code, slug=slug)
    res = list(current_flask_taxonomies.filter_term(term_identification))
    assert isinstance(res[0].extra_data["list"], list)
Ejemplo n.º 9
0
def patch_taxonomy_term(code=None,
                        slug=None,
                        prefer=None,
                        page=None,
                        size=None,
                        q=None):
    if q:
        json_abort(
            422, {
                'message':
                'Query not appropriate when creating or updating term',
                'reason': 'search-query-not-allowed'
            })
    taxonomy = current_flask_taxonomies.get_taxonomy(code, fail=False)
    if not taxonomy:
        json_abort(404, {})
    prefer = taxonomy.merge_select(prefer)

    if INCLUDE_DELETED in prefer:
        status_cond = sqlalchemy.sql.true()
    else:
        status_cond = TaxonomyTerm.status == TermStatusEnum.alive

    ti = TermIdentification(taxonomy=code, slug=slug)
    term = current_flask_taxonomies.filter_term(
        ti, status_cond=status_cond).one_or_none()

    if not term:
        abort(404)

    current_flask_taxonomies.permissions.taxonomy_term_update.enforce(
        request=request, taxonomy=taxonomy, term=term)

    current_flask_taxonomies.update_term(
        term,
        status_cond=status_cond,
        extra_data=request.json,
        patch=True,
        status=TermStatusEnum.alive  # make it alive if it  was deleted
    )
    current_flask_taxonomies.commit()

    return get_taxonomy_term(code=code,
                             slug=slug,
                             prefer=prefer,
                             page=page,
                             size=size)
Ejemplo n.º 10
0
def taxonomy_move_term(code=None,
                       slug=None,
                       prefer=None,
                       page=None,
                       size=None,
                       destination='',
                       rename='',
                       q=None):
    """Move term into a new parent or rename it."""
    if q:
        json_abort(
            422, {
                'message': 'Query not appropriate when moving term',
                'reason': 'search-query-not-allowed'
            })

    try:
        taxonomy = current_flask_taxonomies.get_taxonomy(code)
        ti = TermIdentification(taxonomy=taxonomy, slug=slug)
        term = current_flask_taxonomies.filter_term(ti).one()

        current_flask_taxonomies.permissions.taxonomy_term_move.enforce(
            request=request,
            taxonomy=taxonomy,
            term=term,
            destination=destination,
            rename=rename)
    except NoResultFound as e:
        return json_abort(404, {})

    if destination:
        if destination.startswith('http'):
            destination_path = urlparse(destination).path
            url_prefix = current_app.config['FLASK_TAXONOMIES_URL_PREFIX']
            if not destination_path.startswith(url_prefix):
                abort(
                    400, 'Destination not part of this server as it '
                    'does not start with config.FLASK_TAXONOMIES_URL_PREFIX')
            destination_path = destination_path[len(url_prefix):]
            destination_path = destination_path.split('/', maxsplit=1)
            if len(destination_path) > 1:
                destination_taxonomy, destination_slug = destination_path
            else:
                destination_taxonomy = destination_path[0]
                destination_slug = None
        else:
            destination_taxonomy = code
            destination_slug = destination
            if destination_slug.startswith('/'):
                destination_slug = destination_slug[1:]
        if not current_flask_taxonomies.filter_term(
                TermIdentification(taxonomy=code, slug=slug)).count():
            abort(404, 'Term %s/%s does not exist' % (code, slug))

        try:
            old_term, new_term = current_flask_taxonomies.move_term(
                TermIdentification(taxonomy=code, slug=slug),
                new_parent=TermIdentification(taxonomy=destination_taxonomy,
                                              slug=destination_slug)
                if destination_slug else '',
                remove_after_delete=False
            )  # do not remove the original node from the database,
            # just mark it as deleted
        except TaxonomyTermBusyError as e:
            return json_abort(412, {'message': str(e), 'reason': 'term-busy'})
    elif rename:
        new_slug = slug
        if new_slug.endswith('/'):
            new_slug = new_slug[:-1]
        if '/' in new_slug:
            new_slug = new_slug.rsplit('/')[0]
            new_slug = new_slug + '/' + rename
        else:
            new_slug = rename
        try:
            old_term, new_term = current_flask_taxonomies.rename_term(
                TermIdentification(taxonomy=code, slug=slug),
                new_slug=new_slug,
                remove_after_delete=False
            )  # do not remove the original node from the database, just mark it as deleted
        except TaxonomyTermBusyError as e:
            return json_abort(412, {'message': str(e), 'reason': 'term-busy'})
        destination_taxonomy = code
    else:
        abort(400, 'Pass either `destination` or `rename` parameters ')
        return  # just to make pycharm happy

    current_flask_taxonomies.commit()

    return get_taxonomy_term(code=destination_taxonomy,
                             slug=new_term.slug,
                             prefer=prefer,
                             page=page,
                             size=size)
Ejemplo n.º 11
0
def get_taxonomy_term(code=None,
                      slug=None,
                      prefer=None,
                      page=None,
                      size=None,
                      status_code=200,
                      q=None):
    try:
        taxonomy = current_flask_taxonomies.get_taxonomy(code)
        prefer = taxonomy.merge_select(prefer)

        current_flask_taxonomies.permissions.taxonomy_term_read.enforce(
            request=request, taxonomy=taxonomy, slug=slug)

        if INCLUDE_DELETED in prefer:
            status_cond = sqlalchemy.sql.true()
        else:
            status_cond = TaxonomyTerm.status == TermStatusEnum.alive

        return_descendants = INCLUDE_DESCENDANTS in prefer

        if return_descendants:
            query = current_flask_taxonomies.descendants_or_self(
                TermIdentification(taxonomy=code, slug=slug),
                levels=prefer.options.get('levels', None),
                status_cond=status_cond,
                return_descendants_count=INCLUDE_DESCENDANTS_COUNT in prefer,
                return_descendants_busy_count=INCLUDE_STATUS in prefer)
        else:
            query = current_flask_taxonomies.filter_term(
                TermIdentification(taxonomy=code, slug=slug),
                status_cond=status_cond,
                return_descendants_count=INCLUDE_DESCENDANTS_COUNT in prefer,
                return_descendants_busy_count=INCLUDE_STATUS in prefer)
        if q:
            query = current_flask_taxonomies.apply_term_query(query, q, code)
        paginator = Paginator(prefer,
                              query,
                              page if return_descendants else None,
                              size if return_descendants else None,
                              json_converter=lambda data: build_descendants(
                                  data, prefer, root_slug=None),
                              allow_empty=INCLUDE_SELF not in prefer,
                              single_result=INCLUDE_SELF in prefer,
                              has_query=q is not None)

        return paginator.jsonify(status_code=status_code)

    except NoResultFound:
        term = current_flask_taxonomies.filter_term(
            TermIdentification(taxonomy=code, slug=slug),
            status_cond=sqlalchemy.sql.true()).one_or_none()
        if not term:
            json_abort(
                404, {
                    "message": "%s was not found on the server" % request.url,
                    "reason": "does-not-exist"
                })
        elif term.obsoleted_by_id:
            obsoleted_by = term.obsoleted_by
            obsoleted_by_links = obsoleted_by.links()
            return Response(
                json.dumps({
                    'links': term.links(representation=prefer).envelope,
                    'status': 'moved'
                }),
                status=301,
                headers={
                    'Location':
                    obsoleted_by_links.headers['self'],
                    'Link':
                    str(
                        LinkHeader([
                            Link(v, rel=k) for k, v in term.links(
                                representation=prefer).envelope.items()
                        ]))
                },
                content_type='application/json')
        else:
            json_abort(
                410, {
                    "message": "%s was not found on the server" % request.url,
                    "reason": "deleted"
                })
    except:
        traceback.print_exc()
        raise
Ejemplo n.º 12
0
def _create_update_taxonomy_term_internal(code,
                                          slug,
                                          prefer,
                                          page,
                                          size,
                                          extra_data,
                                          if_none_match=False,
                                          if_match=False):
    try:
        taxonomy = current_flask_taxonomies.get_taxonomy(code)
        prefer = taxonomy.merge_select(prefer)

        if INCLUDE_DELETED in prefer:
            status_cond = sqlalchemy.sql.true()
        else:
            status_cond = TaxonomyTerm.status == TermStatusEnum.alive

        slug = '/'.join(slugify(x) for x in slug.split('/'))

        ti = TermIdentification(taxonomy=code, slug=slug)
        term = original_term = current_flask_taxonomies.filter_term(
            ti, status_cond=sqlalchemy.sql.true()).one_or_none()

        if term and INCLUDE_DELETED not in prefer:
            if term.status != TermStatusEnum.alive:
                term = None

        if if_none_match and term:
            json_abort(
                412, {
                    'message':
                    'The taxonomy already contains a term on this slug. ' +
                    'As If-None-Match: \'*\' has been requested, not modifying the term',
                    'reason':
                    'term-exists'
                })

        if if_match and not term:
            json_abort(
                412, {
                    'message':
                    'The taxonomy does not contain a term on this slug. ' +
                    'As If-Match: \'*\' has been requested, not creating a new term',
                    'reason':
                    'term-does-not-exist'
                })

        if term:
            current_flask_taxonomies.permissions.taxonomy_term_update.enforce(
                request=request, taxonomy=taxonomy, term=term)
            current_flask_taxonomies.update_term(term,
                                                 status_cond=status_cond,
                                                 extra_data=extra_data)
            status_code = 200
        else:
            if original_term:
                # there is a deleted term, so return a 409 Conflict
                json_abort(
                    409, {
                        'message':
                        'The taxonomy already contains a deleted term on this slug. '
                        'To reuse the term, repeat the operation with `del` in '
                        'representation:include.',
                        'reason':
                        'deleted-term-exists'
                    })

            current_flask_taxonomies.permissions.taxonomy_term_create.enforce(
                request=request, taxonomy=taxonomy, slug=slug)
            current_flask_taxonomies.create_term(ti, extra_data=extra_data)
            status_code = 201

        current_flask_taxonomies.commit()

        return get_taxonomy_term(code=code,
                                 slug=slug,
                                 prefer=prefer,
                                 page=page,
                                 size=size,
                                 status_code=status_code)

    except NoResultFound:
        json_abort(404, {})
    except:
        traceback.print_exc()
        raise