Merge pull request #91 from Simple2B/syvat/breadcrumbs

Syvat/breadcrumbs
This commit is contained in:
Svyatoslav Artymovych 2023-05-30 16:46:11 +03:00 committed by GitHub
commit 3c4137052e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 92 additions and 84 deletions

View File

@ -11,6 +11,7 @@
"**/.venv/*/**": true "**/.venv/*/**": true
}, },
"cSpell.words": [ "cSpell.words": [
"backref",
"bookname", "bookname",
"Btns", "Btns",
"CLEANR", "CLEANR",

View File

@ -5,11 +5,25 @@ from app import models as m, db
from app import schema as s from app import schema as s
def create_collections_breadcrumb(
bread_crumbs: list[s.BreadCrumb], collection: m.Collection
) -> list[s.BreadCrumb]:
bread_crumbs += [
s.BreadCrumb(
type=s.BreadCrumbType.Collection,
url="",
label=collection.label,
)
]
if collection.parent and not collection.parent.is_root:
create_collections_breadcrumb(bread_crumbs, collection.parent)
def create_breadcrumbs( def create_breadcrumbs(
book_id: int, book_id: int,
collection_path: tuple[int],
section_id: int = 0, section_id: int = 0,
interpretation_id: int = 0, collection_id: int = 0,
) -> list[s.BreadCrumb]: ) -> list[s.BreadCrumb]:
""" """
How breadcrumbs look like: How breadcrumbs look like:
@ -52,39 +66,32 @@ def create_breadcrumbs(
) )
] ]
for index, collection_id in enumerate(collection_path): section: m.Section = None
if collection_id is None: if section_id:
continue
collection: m.Collection = db.session.get(m.Collection, collection_id)
if index == 0:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Collection,
url="",
label=collection.label,
)
]
elif index == 1:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Section,
url="",
label=collection.label,
)
]
if section_id and collection_path:
section: m.Section = db.session.get(m.Section, section_id) section: m.Section = db.session.get(m.Section, section_id)
if collection_id and not section:
collections_crumbs = []
collection: m.Collection = db.session.get(m.Collection, collection_id)
if not collection.is_root:
create_collections_breadcrumb(collections_crumbs, collection)
collections_crumbs.reverse()
crumples += collections_crumbs
if section:
collections_crumbs = []
collection: m.Collection = db.session.get(m.Collection, section.collection_id)
if not collection.is_root:
create_collections_breadcrumb(collections_crumbs, collection)
collections_crumbs.reverse()
crumples += collections_crumbs
crumples += [ crumples += [
s.BreadCrumb( s.BreadCrumb(
type=s.BreadCrumbType.Section, type=s.BreadCrumbType.Section,
url=url_for( url=url_for(
"book.interpretation_view", "book.interpretation_view",
book_id=book_id, book_id=book_id,
collection_id=collection_path[0],
sub_collection_id=collection_path[-1]
if len(collection_path) == 2
else collection_path[0],
section_id=section_id, section_id=section_id,
), ),
label=section.label, label=section.label,

View File

@ -6,6 +6,7 @@ from app.controllers import create_breadcrumbs
from .interpretation import Interpretation from .interpretation import Interpretation
from .comment import Comment from .comment import Comment
from .interpretation_vote import InterpretationVote from .interpretation_vote import InterpretationVote
from app import schema as s
class Section(BaseModel): class Section(BaseModel):
@ -46,16 +47,15 @@ class Section(BaseModel):
@property @property
def breadcrumbs_path(self): def breadcrumbs_path(self):
parent = self.collection breadcrumbs_path = create_breadcrumbs(
grand_parent = parent.parent book_id=self.book_id, collection_id=self.collection.id
if grand_parent.is_root: )
collection_path = (parent.id,) if len(breadcrumbs_path) > 5:
else: breadcrumbs_path = (
collection_path = ( breadcrumbs_path[:3]
grand_parent.id, + [s.BreadCrumb(type=s.BreadCrumbType.Splitter, url="", label="...")]
parent.id, + breadcrumbs_path[-2:]
) )
breadcrumbs_path = create_breadcrumbs(self.book_id, collection_path)
return breadcrumbs_path return breadcrumbs_path
@property @property

View File

@ -11,6 +11,7 @@ class BreadCrumbType(enum.StrEnum):
Collection = "Collection" Collection = "Collection"
Section = "Section" Section = "Section"
Interpretation = "Interpretation" Interpretation = "Interpretation"
Splitter = "Splitter"
class BreadCrumb(BaseModel): class BreadCrumb(BaseModel):

View File

@ -32,12 +32,15 @@
<div data="sub-collection-context-menu-{{sub_collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700"> <div data="sub-collection-context-menu-{{sub_collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200"> <ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
{% if sub_collection.is_leaf and not sub_collection.children %}
<li> <li>
<button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button> <button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button>
</li> </li>
{% else %}
<li> <li>
<button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button> <button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button>
</li> </li>
{% endif %}
</ul> </ul>
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200"> <ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
<li> <li>

View File

@ -1,26 +1,33 @@
<!-- prettier-ignore --> <!-- prettier-ignore -->
<ol class="inline-flex items-center overflow-x-scroll md:overflow-auto p-0"> <ol class="inline-flex items-center overflow-x-scroll md:overflow-auto p-0">
{% for breadcrumb in local_breadcrumbs if breadcrumb.type != "MyBookList" and breadcrumb.type != "AuthorBookList" %} {% for breadcrumb in local_breadcrumbs if breadcrumb.type != "MyBookList" and breadcrumb.type != "AuthorBookList" %}
<li class="inline-flex items-center align-middle justify-center"> <li class="inline-flex items-center align-middle justify-center">
{% if not loop.index==local_breadcrumbs|length-1 %} {% if not loop.index==local_breadcrumbs|length-1 %}
<a href="{{ breadcrumb.url }}" class="inline-flex text-xs font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom"> <a href="{{ breadcrumb.url }}" class="inline-flex text-xs font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
{% else %} {% else %}
<span class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom"> <span class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
{% endif %} {% endif %}
<div id="breadcrumb-{{loop.index}}-tooltip" role="tooltip" class="delay-100 absolute z-[100] invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-700 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
{{ breadcrumb.label }} {% if breadcrumb.type != "Splitter" %}
<div class="tooltip-arrow" data-popper-arrow></div> <div id="breadcrumb-{{loop.index}}-tooltip" role="tooltip" class="delay-100 absolute z-[100] invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-700 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
</div> {{ breadcrumb.label }}
<!-- prettier-ignore --> <div class="tooltip-arrow" data-popper-arrow></div>
<span class="select-none">{{ breadcrumb.label }}</span> </div>
{% if not loop.index==local_breadcrumbs|length %} {% endif %}
</a>
{% else %} <!-- prettier-ignore -->
</span> <span class="select-none">{{ breadcrumb.label }}</span>
{% endif %}
{% if not loop.index==local_breadcrumbs|length-1 %} {% if not loop.index==local_breadcrumbs|length %}
<svg aria-hidden="true" class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> </svg> </a>
{% endif %} {% else %}
</li> </span>
{% endif %}
{% if not loop.index==local_breadcrumbs|length-1 %}
<svg aria-hidden="true" class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> </svg>
{% endif %}
</li>
{% endfor %} {% endfor %}
</ol> </ol>

View File

@ -21,7 +21,7 @@ from .bp import bp
@bp.route("/<int:book_id>/collections", methods=["GET"]) @bp.route("/<int:book_id>/collections", methods=["GET"])
def collection_view(book_id: int): def collection_view(book_id: int):
book = db.session.get(m.Book, book_id) book = db.session.get(m.Book, book_id)
breadcrumbs = create_breadcrumbs(book_id=book_id, collection_path=()) breadcrumbs = create_breadcrumbs(book_id=book_id)
if not book or book.is_deleted: if not book or book.is_deleted:
log(log.WARNING, "Book with id [%s] not found", book_id) log(log.WARNING, "Book with id [%s] not found", book_id)
flash("Book not found", "danger") flash("Book not found", "danger")

View File

@ -38,20 +38,14 @@ def interpretation_view(
book_id=book_id, book_id=book_id,
) )
) )
# FIXME breadcrumbs breadcrumbs = create_breadcrumbs(
# breadcrumbs = create_breadcrumbs( book_id=book_id, section_id=section_id, collection_id=section.collection.id
# book_id=book_id, )
# collection_path=(
# collection_id,
# sub_collection_id,
# ),
# section_id=section_id,
# )
return render_template( return render_template(
"book/interpretation_view.html", "book/interpretation_view.html",
book=book, book=book,
section=section, section=section,
breadcrumbs=[], breadcrumbs=breadcrumbs,
) )
@ -191,20 +185,15 @@ def qa_view(book_id: int, interpretation_id: int):
flash("Interpretation not found", "danger") flash("Interpretation not found", "danger")
return redirect(url_for("book.collection_view", book_id=book_id)) return redirect(url_for("book.collection_view", book_id=book_id))
# FIXME breadcrumbs = create_breadcrumbs(
# breadcrumbs = create_breadcrumbs( book_id=book_id,
# book_id=book_id, collection_id=interpretation.section.collection.id,
# collection_path=( section_id=interpretation.section.id,
# collection_id, )
# sub_collection_id,
# ),
# section_id=section_id,
# interpretation_id=interpretation.id,
# )
return render_template( return render_template(
"book/qa_view.html", "book/qa_view.html",
book=book, book=book,
section=interpretation.section, section=interpretation.section,
interpretation=interpretation, interpretation=interpretation,
breadcrumbs=[], breadcrumbs=breadcrumbs,
) )

View File

@ -458,7 +458,7 @@ def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner):
label="Test SubCollection #1 Label" label="Test SubCollection #1 Label"
).first() ).first()
assert sub_collection assert sub_collection
assert sub_collection.is_leaf assert not sub_collection.is_leaf
assert sub_collection.parent_id == collection.id assert sub_collection.parent_id == collection.id
m.Collection( m.Collection(

View File

@ -6,13 +6,13 @@ from app import models as m, db
def test_breadcrumbs(runner: FlaskCliRunner, app): def test_breadcrumbs(runner: FlaskCliRunner, app):
runner.invoke(args=["db-populate"]) runner.invoke(args=["db-populate"])
with app.app_context(), app.test_request_context(): with app.app_context(), app.test_request_context():
res = create_breadcrumbs(1, (1,), 1) res = create_breadcrumbs(book_id=1, collection_id=1, section_id=1)
assert len(res) == 4 assert len(res) == 4
book: m.Book = db.session.get(m.Book, 1) book: m.Book = db.session.get(m.Book, 1)
assert book assert book
assert book.owner.username in res[0].label assert book.owner.username in res[0].label
assert res[1].label == book.label assert res[1].label == book.label
with app.app_context(), app.test_request_context(): with app.app_context(), app.test_request_context():
res = create_breadcrumbs(1, (), 1) res = create_breadcrumbs(book_id=1, section_id=1)
assert res assert res
assert len(res) == 2 assert len(res) == 4