Skip to content

Commit b3e65e9

Browse files
top-books endpoint
Fixed: - Slow initial page load of index due to top-k being slow
1 parent 33a769e commit b3e65e9

4 files changed

Lines changed: 77 additions & 41 deletions

File tree

src/presentation/templates/index.html

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -57,56 +57,29 @@ <h3 class="font-semibold">Digital Library</h3>
5757
<div class="flex justify-between items-center mb-8">
5858
<h2 class="text-3xl font-bold text-gray-900">Top Rated Books</h2>
5959
<div class="mt-0">
60-
<form method="get" action="{% url 'index' %}" class="flex items-center space-x-4">
60+
<form class="flex items-center space-x-4">
6161
<label for="k-value" class="font-medium text-gray-700">Show top:</label>
62-
<select id="k-value" name="k" class="rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" onchange="this.form.submit()">
62+
<select id="k-value" name="k" class="rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
6363
<option value="5" {% if k_value == 5 %}selected{% endif %}>5 books</option>
6464
<option value="10" {% if k_value == 10 %}selected{% endif %}>10 books</option>
6565
<option value="25" {% if k_value == 25 %}selected{% endif %}>25 books</option>
6666
<option value="50" {% if k_value == 50 %}selected{% endif %}>50 books</option>
6767
</select>
6868
</form>
6969
</div>
70+
</div>
71+
<!-- Loading state -->
72+
<div id="loading-books" class="text-center py-12">
73+
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
74+
<p class="mt-4 text-gray-600">Loading top books...</p>
7075
</div>
7176

72-
{% if top_books %}
73-
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-8">
74-
{% for book in top_books %}
75-
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
76-
<a href="{% url 'book_details' book_id=book.id %}">
77-
{% if book.image %}
78-
<img src="{{ book.image }}" alt="{{ book.title }}" class="w-full h-64 object-cover">
79-
{% else %}
80-
<div class="w-full h-64 bg-gray-200 flex items-center justify-center">
81-
<span class="text-gray-500">No Image</span>
82-
</div>
83-
{% endif %}
84-
</a>
85-
<div class="p-4">
86-
<h3 class="text-lg font-semibold mb-1" title="{{ book.title }}">
87-
<a href="{% url 'book_details' book_id=book.id %}" class="hover:text-blue-600 line-clamp-2 min-h-[3rem]" style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;">{{ book.title }}</a>
88-
</h3>
89-
<p class="text-sm text-gray-600 truncate" title="{{ book.authors|default:'Unknown Author' }}">
90-
{{ book.authors|default:"Unknown Author" }}
91-
</p>
92-
<div class="flex items-center justify-between mt-2">
93-
<div class="flex items-center">
94-
{% if book.rating %}
95-
<span class="text-yellow-400"></span>
96-
<span class="ml-1 text-sm text-gray-700">{{ book.rating|floatformat:1 }}</span>
97-
{% endif %}
98-
</div>
99-
<div class="text-xs text-gray-500">
100-
Score: {{ book.score|floatformat:2 }}
101-
</div>
102-
</div>
103-
</div>
77+
<!-- Books container -->
78+
<div id="books-container" class="hidden">
79+
<div id="books-grid" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-8">
10480
</div>
105-
{% endfor %}
81+
<div id="no-books" class="hidden text-center text-gray-600">No top books to display at the moment.</div>
10682
</div>
107-
{% else %}
108-
<p class="text-center text-gray-600">No top books to display at the moment.</p>
109-
{% endif %}
11083
</div>
11184

11285
<!-- Existing Feature Sections (can be kept, modified, or removed) -->
@@ -161,5 +134,29 @@ <h2 class="text-3xl font-bold mb-4">Join Our Growing Community</h2>
161134
</div>
162135
<p class="text-gray-600 mt-8">Start your free 30-day trial. No credit card required.</p>
163136
</div>
164-
</main>
137+
</div>
138+
139+
{% endblock %}
140+
141+
{% block tail_js %}
142+
{{ block.super }}
143+
<script>
144+
document.getElementById('k-value').addEventListener('change', function() {
145+
document.getElementById('loading-books').classList.remove('hidden');
146+
document.getElementById('books-container').classList.add('hidden');
147+
148+
fetch(`{% url 'top_books' %}?k=${this.value}`)
149+
.then(r => r.json())
150+
.then(data => {
151+
document.getElementById('loading-books').classList.add('hidden');
152+
document.getElementById('books-container').classList.remove('hidden');
153+
document.getElementById('books-grid').innerHTML = data.html;
154+
});
155+
});
156+
157+
// Load initial books
158+
document.addEventListener('DOMContentLoaded', () => {
159+
document.getElementById('k-value').dispatchEvent(new Event('change'));
160+
});
161+
</script>
165162
{% endblock %}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{% for book in books %}
2+
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
3+
<a href="{% url 'book_details' book_id=book.id %}">
4+
{% if book.image %}
5+
<img src="{{ book.image }}" alt="{{ book.title }}" class="w-full h-64 object-cover">
6+
{% else %}
7+
<div class="w-full h-64 bg-gray-200 flex items-center justify-center">
8+
<span class="text-gray-500">No Image</span>
9+
</div>
10+
{% endif %}
11+
</a>
12+
<div class="p-4">
13+
<h3 class="text-lg font-semibold mb-1">
14+
<a href="{% url 'book_details' book_id=book.id %}" class="hover:text-blue-600" title="{{ book.title }}">{{ book.title }}</a>
15+
</h3>
16+
<p class="text-sm text-gray-600 truncate">{{ book.authors|default:"Unknown Author" }}</p>
17+
<div class="flex items-center justify-between mt-2">
18+
{% if book.rating %}
19+
<div class="flex items-center">
20+
<span class="text-yellow-400"></span>
21+
<span class="ml-1 text-sm text-gray-700">{{ book.rating|floatformat:1 }}</span>
22+
</div>
23+
{% else %}
24+
<div></div>
25+
{% endif %}
26+
<div class="text-xs text-gray-500">Score: {{ book.score|floatformat:2 }}</div>
27+
</div>
28+
</div>
29+
</div>
30+
{% endfor %}

src/presentation/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
path("search/", views.search, name="search"),
88
path("book/<str:book_id>/", views.book_details, name="book_details"),
99
path("autocomplete/", views.autocomplete, name="autocomplete"),
10+
path("top-books/", views.top_books, name="top_books"),
1011
path("about/", views.about, name="about"),
1112
path("signup/", views.signup_view, name="signup"),
1213
path("profile/", views.profile_view, name="profile"),

src/presentation/views.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.core.paginator import Paginator
55
from django.http import JsonResponse
66
from django.shortcuts import get_object_or_404, redirect, render
7+
from django.template.loader import render_to_string
78
from django.utils.cache import patch_cache_control, patch_vary_headers
89

910
from business_logic.bst import BST
@@ -22,10 +23,8 @@ def cache_page(response):
2223

2324
def index(request):
2425
k = int(request.GET.get("k", 10)) # Get k parameter, default to 10
25-
top_books = BookRanker.get_top_k(k) # Fetch top k books
2626

2727
context = {
28-
"top_books": top_books,
2928
"k_value": k,
3029
"title": f"Top {k} Books",
3130
}
@@ -35,6 +34,15 @@ def index(request):
3534
return response
3635

3736

37+
def top_books(request):
38+
k = int(request.GET.get("k", 10))
39+
top_books = BookRanker.get_top_k(k)
40+
html = render_to_string("top_books.html", {"books": top_books}, request=request)
41+
response = JsonResponse({"html": html})
42+
cache_page(response)
43+
return response
44+
45+
3846
def search(request):
3947
page = request.GET.get("page", 1)
4048
per_page = 100

0 commit comments

Comments
 (0)