Exemple #1
0
def export_taxonomy(taxonomy_code: str):
    # get cwd path
    cwd = pathlib.Path().absolute()
    path = str(cwd / f"{taxonomy_code}.csv")

    # get taxonomy flatten dicts (header and content)
    taxonomy = current_flask_taxonomies.get_taxonomy(taxonomy_code)
    first_level_list = current_flask_taxonomies.list_taxonomy(taxonomy,
                                                              levels=1).all()
    taxonomy_header_dict = get_taxonomy_header_dict(taxonomy)
    taxonomy_terms_list = get_taxonomy_terms_list(first_level_list)

    csv_path = convert_to_csv(path, taxonomy_terms_list)
    excel_path = convert_csv_to_excel(path, taxonomy_header_dict)
    return csv_path, excel_path
Exemple #2
0
def test_get_taxonomy_terms_list(app, db):
    file_path = pathlib.Path(__file__).parent.absolute()
    data_path = file_path / "data" / "licenses_v2.xlsx"
    str_path = str(data_path)
    import_taxonomy(str_path)

    taxonomy = current_flask_taxonomies.get_taxonomy("licenses")
    first_level = current_flask_taxonomies.list_taxonomy(taxonomy,
                                                         levels=1).all()
    res = get_taxonomy_terms_list(first_level)
    assert res[0]["level"] == 1
    assert res[1]["level"] == 2
    assert res[2]["level"] == 3
    assert res[3]["level"] == 3
    assert res[8]["level"] == 2
Exemple #3
0
def get_taxonomy_json(
        code=None,
        slug=None,
        prefer: Representation = Representation("representation"),
        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
Exemple #4
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)
Exemple #5
0
def import_countries(db):
    # with db.session.begin_nested():
    try:
        Base.metadata.create_all(db.engine)
    except:
        pass

    tax = current_flask_taxonomies.get_taxonomy(code='country', fail=False)
    if tax:
        return

    tax = current_flask_taxonomies.create_taxonomy(
        code='country',
        extra_data={'title': 'List of countries'},
        url='https://www.kaggle.com/nikitagrec/world-capitals-gps/data')

    continents = {}
    with open(os.path.join(os.path.dirname(__file__), 'countries.csv'),
              'r') as f:
        rdr = csv.DictReader(f)
        for row in rdr:
            try:
                continent = row['ContinentName']
                country = row['CountryCode']
                if continent not in continents:
                    print('Creating continent', continent.lower())
                    continent_term = current_flask_taxonomies.create_term(
                        TermIdentification(taxonomy=tax,
                                           slug=continent.lower().replace(
                                               ' ', '-')), )
                    continents[continent] = continent_term

                slug = '%s/%s' % (continent.lower(), country.lower())
                slug = slug.replace(' ', '-')
                print('Importing', slug)
                current_flask_taxonomies.create_term(TermIdentification(
                    taxonomy=tax, slug=slug),
                                                     extra_data=row)
            except:
                traceback.print_exc()
                return
    try:
        db.session.commit()
    except:
        traceback.print_exc()
        sys.exit(1)
Exemple #6
0
def delete_taxonomy(code=None):
    """
    Deletes a taxonomy.

    Note: this call is destructive in a sense that all its terms, regardless if used or not,
    are deleted as well. A tight user permissions should be employed.
    """
    try:
        tax = current_flask_taxonomies.get_taxonomy(code=code)
    except NoResultFound:
        json_abort(404, {})
        return  # make pycharm happy

    current_flask_taxonomies.permissions.taxonomy_delete.enforce(
        request=request, code=code)
    current_flask_taxonomies.delete_taxonomy(tax)
    current_flask_taxonomies.commit()
    return Response(status=204)
def create_update_taxonomy(data, drop) -> Taxonomy:
    tax_dict = convert_data_to_dict(data)[0]
    if 'code' not in tax_dict:
        raise ValueError('Taxonomy does not contain "code"')
    code = tax_dict.pop('code')
    taxonomy = current_flask_taxonomies.get_taxonomy(code, fail=False)
    if taxonomy and drop:
        current_flask_taxonomies.delete_taxonomy(taxonomy)
        taxonomy = None

    if taxonomy:
        merged_dict = taxonomy.extra_data
        merged_dict.update(tax_dict)
        current_flask_taxonomies.update_taxonomy(taxonomy, merged_dict)
    else:
        taxonomy = current_flask_taxonomies.create_taxonomy(code, extra_data=tax_dict)
    db.session.commit()
    return taxonomy
Exemple #8
0
def create_update_taxonomy(code=None,
                           prefer=None,
                           page=None,
                           size=None,
                           q=None):
    if q:
        json_abort(
            422, {
                'message':
                'Query not appropriate when creating or updating taxonomy',
                'reason': 'search-query-not-allowed'
            })
    tax = current_flask_taxonomies.get_taxonomy(code=code, fail=False)

    if tax:
        current_flask_taxonomies.permissions.taxonomy_update.enforce(
            request=request, taxonomy=tax)
    else:
        current_flask_taxonomies.permissions.taxonomy_create.enforce(
            request=request, code=code)

    data = request.json
    url = data.pop('url', None)
    select = data.pop('select', None)
    if not tax:
        current_flask_taxonomies.create_taxonomy(code=code,
                                                 extra_data=request.json,
                                                 url=url,
                                                 select=select)
        status_code = 201
    else:
        current_flask_taxonomies.update_taxonomy(tax,
                                                 extra_data=request.json,
                                                 url=url,
                                                 select=select)
        status_code = 200
    current_flask_taxonomies.commit()
    return get_taxonomy(code,
                        prefer=prefer,
                        page=page,
                        size=size,
                        status_code=status_code)
Exemple #9
0
def test_taxonomy_term_moved(app, db, taxonomy_tree, test_record):
    taxonomy = current_flask_taxonomies.get_taxonomy("test_taxonomy")
    terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    old_record = Record.get_record(id_=test_record.id)
    old_taxonomy = old_record["taxonomy"]
    assert old_taxonomy == [{
        'is_ancestor': True,
        'level': 1,
        'links': {
            'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a'
        },
        'test': 'extra_data'
    },
        {
            'is_ancestor': True,
            'level': 2,
            'links': {
                'parent':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a',
                'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b'
            },
            'test': 'extra_data'
        },
        {
            'is_ancestor': False,
            'level': 3,
            'links': {
                'parent':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b',
                'self':
                    'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b/c'
            },
            'test': 'extra_data'
        }]
    ti = TermIdentification(term=terms[2])
    current_flask_taxonomies.move_term(ti, new_parent=terms[0], remove_after_delete=False)
    db.session.commit()
    new_record = Record.get_record(id_=test_record.id)
    new_taxonomy = new_record["taxonomy"]
    new_terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    assert new_terms[-1].parent_id == 1
Exemple #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)
Exemple #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
Exemple #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
Exemple #13
0
def test_taxonomy_term_delete(app, db, taxonomy_tree):
    taxonomy = current_flask_taxonomies.get_taxonomy("test_taxonomy")
    terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    term = terms[1]
    ti = TermIdentification(term=term)
    current_flask_taxonomies.delete_term(ti)
Exemple #14
0
def test_taxonomy_term_update(app, db, taxonomy_tree, test_record):
    taxonomy = current_flask_taxonomies.get_taxonomy("test_taxonomy")
    terms = current_flask_taxonomies.list_taxonomy(taxonomy).all()
    old_record = Record.get_record(id_=test_record.id)
    assert old_record == {
        'pid': 1,
        'taxonomy': [{
            'is_ancestor': True,
            'level': 1,
            'links': {'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a'},
            'test': 'extra_data'
        },
            {
                'is_ancestor': True,
                'level': 2,
                'links': {
                    'parent': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a',
                    'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b'
                },
                'test': 'extra_data'
            },
            {
                'is_ancestor': False,
                'level': 3,
                'links': {
                    'parent': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b',
                    'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b/c'
                },
                'test': 'extra_data'
            }],
        'title': 'record 1'
    }
    term = terms[-1]
    current_flask_taxonomies.update_term(term, extra_data={"new_data": "changed extra data"})
    new_record = Record.get_record(id_=test_record.id)
    assert new_record == {
        'pid': 1,
        'taxonomy': [{
                         'is_ancestor': True,
                         'level': 1,
                         'links': {'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a'},
                         'test': 'extra_data'
                     },
                     {
                         'is_ancestor': True,
                         'level': 2,
                         'links': {
                             'parent': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a',
                             'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b'
                         },
                         'test': 'extra_data'
                     },
                     {
                         'is_ancestor': False,
                         'level': 3,
                         'links': {
                             'parent': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b',
                             'self': 'http://127.0.0.1:5000/2.0/taxonomies/test_taxonomy/a/b/c'
                         },
                         'new_data': 'changed extra data',
                         'test': 'extra_data'
                     }],
        'title': 'record 1'
    }