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)
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
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))
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()
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
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
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
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)
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)
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)
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
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