NextChaptr is a minimalist book-tracking web app that helps readers organize and reflect on their reading journey. Developed as a full-stack project, it highlights key features such as book categorization, rating, and reviewing with modern web technologies.
Unlike feature-heavy platforms, NextChaptr focuses on simplicity, allowing users to manage reading lists (To Read, Reading, Read), rate completed books, and engage through reviews in a clean, distraction-free interface.
🔗 Live App: https://nextchaptr-f17e381cb655.herokuapp.com/
- Target Audience
- Site Goal
- Features
- Design
- Models
- Django Project Structure
- Requirements Overview
- Testing
- Bug Fixes
- Sprint Planning
- Deployment
- Credits & References
- Acknoledgements
NextChaptr is built for readers who want a focused, user-friendly space to log their reading habits without social clutter or unnecessary complexity.
- Avid Individual Readers: book enthusiasts seeking a simple, streamlined platform to log their reading journey.
- Personal Growth & Self-Reflection Users: individuals who track reading patterns for insights into interests, habits, and achievements.
- Busy Readers & Lifelong Learners: people looking to organize reading lists, track progress effortlessly, and fit reading into daily routines.
Promote Literacy
- Provide a simple, engaging platform that motivates users to read more consistently.
- Support users in building sustainable reading habits through progress tracking.
Promote Culture & Community
- Highlight the cultural value of reading as both a personal and shared experience.
- Encourage conversations around books, authors, and ideas to foster community.
Support Reading Management in Busy Routines
- Help users organize their reading lists and track progress at a glance.
- Offer a lightweight, distraction-free space to balance reading with daily responsibilities.
A welcoming, responsive landing experience that introduces NextChaptr and funnels visitors into core actions.
- Responsive hero banner: A full-width banner sets the brand tone and provides visual context.
- Clear purpose blurb: A brief description explains what NextChaptr does and who it’s for.
- Primary actions up front: Prominent entry points to search books or sign up/log in.
- Browse by genre: Category tiles link directly to filtered search views.
Book discovery is powered by the Google Books API, allowing users to explore a vast catalog with flexible search options.
- Keyword or field-specific search: Search by general keywords or refine by title, author, or genre.
- Smart query handling: Automatically applies the correct Google Books operators.
- Clean results: Thumbnails, titles, and authors in a browsable layout.
- First-party cover images: A
/cover/proxy serves covers from our own domain to avoid third-party requests/cookies and improve Lighthouse privacy scores. - Pagination: Navigate large result sets with ease.
- Resilient design: Handles API/network errors gracefully.
- Caching: Details are cached for ~1 hour to reduce API calls.
- Search bar design: The search form uses a visible select element for query fields. Design inspired by Booking.com and Goodreads.
Each book has a dedicated detail page with enriched information for readers.
- Comprehensive metadata: Title, subtitle, authors, publisher, publication date, page count, categories, description, and cover image.
- First-party cover images: Served via the
/cover/proxy to avoid third-party cookies. - Performance boost: Uses Django caching to store details for one hour, reducing API calls while keeping data fresh.
- Seamless access: Linked directly from search results for a smooth browsing experience.
- Page design: Follows common hierarchies used in other content hubs (e.g., IMDb, Letterboxd, Goodreads, Amazon). The image appears in the top-left, core metadata at the top, primary actions and ratings below the cover, and longer-form reviews lower on the page.
Track progress with a simple three-state flow.
- Three statuses: To Read, Reading, Read (
TO_READ,READING,READ). One status per(user, book). - Inline controls: Posting a valid status creates/updates a
ReadingStatus; invalid choices show a friendly error and redirect back to the detail page. - Remove from library: Sending
status=NONEdeletes the status and archives any rating/review for that book (kept for analytics, hidden from UI). - Book FK safety: Status changes ensure a
Bookrow exists viafetch_or_refresh_book(...). - Integrity & performance: Unique constraint on
(user, book)with helpful indexes for library queries. - UX feedback: Success/error messages confirm actions and return users to the originating page.
- Button design: To Read is emphasized as the primary action (a common first step). Other statuses are accessible via a dropdown (the arrow hints at additional options).
Provide quick feedback and aggregate community sentiment.
- Star ratings (1–5): Authenticated users can create/update a rating;
rating=0removes it. Values outside 1–5 display a validation message. - Status invariant: Posting a rating ensures a
ReadingStatusexists (defaults to READ if missing). Existing statuses are respected. - Archive on removal: Removing a status archives the user’s rating (
is_archived=True, timestamped) so it disappears from UI but remains for analytics. Re-posting unarchives the most recent row. - Averages & counts: Helpers compute average rating and total ratings for display on the book detail.
- User feedback: Notifications confirm when a rating is saved or updated.
- Rating buttons design: The star format and hover animation are inspired by Letterboxd and Goodreads for familiarity.
Let readers share longer-form thoughts with an edit-friendly flow.
- Single, text-based review: One review per user/book.
- Inline compose, edit & delete: No review → a form appears. With a review → Edit and Delete buttons.
- One-per-book guarantee: A unique
(user, book)constraint updates on resubmission (no duplicates). - Status invariant: Posting a review ensures a status row exists (defaults to READ if missing).
- Archive on removal: Removing a status archives the user’s review (
is_archived=True, timestamped). Re-posting unarchives the most recent row. - Ownership guard: Only the author can delete their review; unauthorized deletes return
403.
A personal library displays all books a user is interested in, with their respective reading status.
- Filter by status: Filter tags at the top of the table allows user to filter by status: All (default), To Read, Reading, and Read.
- Sorting: Table default sorting is by updated timestamp. Optional sorting by Status (asc/desc) available by clicking on column header.
- First-party cover images: Served via
/cover/to avoid third-party cookies. - UX note: “Remove from Library” opens a confirmation dialog that explains rating/review will be removed from the profile (archived, not permanently deleted).
- Dynamic updates: The library view reflects changes immediately.
- Links to details: Each book links to its detail page.
Critical actions (e.g., removing a book from the library or deleting a review) are protected by confirmation modals.
- Clear messaging: Modals explain consequences.
- User control: Confirm or cancel to retain control.
- Reusable component: Implemented as a template partial for consistency.
- Design rationale: Follows UX best practices to prevent accidental destructive actions.
User authentication is powered by Django Allauth for a secure and reliable account experience.
-
Sign up: New users can create an account. The template is customized to match the site’s brand.
-
Login / Logout: Access personal features and log out securely.
-
Consistent UI: Allauth templates are adapted to the project’s design system.
-
Actions require authentication: While browsing and discovery is allowed to all, only authenticated users can add a book to their library, rate or review books.
Permissions Matrix
Action Guest Authenticated Add to Library ✖ ✔ Change Status ✖ ✔ Rate Book ✖ ✔ Write Review ✖ ✔ Delete Review ✖ ✔ (own only)
Custom error pages maintain brand styling.
- Clear error: Pages state the error type.
- Redirects: Buttons return users to home or the previous page.
- Incident number: Included for help-center communication.
- Books admin shows minimal cached metadata (id, title, authors, thumbnail, language, published date, fetch markers).
- Reading statuses admin shows
(user, book_id, title, status)with a link to the Google Books page. - Rating admin lists denormalized book titles with quick links to Google Books; supports searching by user, book ID, and title.
- Review admin lists support searching by user, book ID/title, and content, with date filters for moderation.
The pairing balances readability and personality. Bitter ensures a comfortable reading experience, Roboto adds modern clarity for interface text, and Abril Fatface adds emphasis.
The palette draws inspiration from nature for a cozy, calming atmosphere, akin to the feeling of reading. Green is the primary color, blue the secondary, and terracotta serves as an accent.
Accessibility has been verified with W3C, Lighthouse, Accessibility Checker and manual checks:
- Color contrast: Verified against WCAG 2.1 AA using Accessibility Checker and Lighthouse
- Keyboard navigation: All interactive elements are reachable in a logical order; skip links added to jump to main content.
- ARIA roles: Removed redundant roles flagged by W3C validator; only essential ARIA attributes remain.
- Error feedback: Forms and destructive actions provide clear feedback and confirmations.
Following a mobile-first approach, wireframes focus on smaller screens. Tablet and desktop designs use the same layout principles while making better use of wider screens, particularly for tables.
The data model balances external metadata (from Google Books) with internal user interactions.
Books are only stored locally if a user explicitly saves or interacts with them, keeping the database lightweight.
Uses Django’s built-in User model for authentication and ownership of records.
Represents a book saved in the system (created only when a user sets a reading status, rates or reviews it).
The primary key is the Google Books volumeId.
Fields
id:CharField, PK (GooglevolumeId)title:CharFieldauthors:TextField(list of author names)thumbnail_url:URLFieldlanguage:CharFieldpublished_date_raw:CharFieldetag:CharFieldlast_modified:DateTimeField(HTTP cache header)last_fetched_at:DateTimeField(defaults to now)created_at:DateTimeField(record creation timestamp)updated_at:DateTimeField(auto-updated)
Meta
- Index on
titlefor faster search.
Methods
needs_refresh(ttl_minutes=1440): checks if book metadata has expired (default TTL: 24 hours).
Cover storage vs. display
The database stores the remote thumbnail_url only; image bytes are not stored.
At render time, templates use a first-party cover proxy to fetch and serve the image from our origin.
This keeps the DB lightweight and eliminates third-party cookies.
(A future enhancement could add an ImageField to persist files if needed.)
Tracks a user's reading status for a specific book.
Fields
user:ForeignKey→Userbook:ForeignKey→Bookstatus:CharFieldwith choices:"TO_READ"→ “To Read”"READING"→ “Reading”"READ"→ “Read”
created_at:DateTimeFieldupdated_at:DateTimeField
Meta
- Unique constraint on
(user, book)→ one status per book per user. - Indexes on
(user, status),(user, book), andstatus.
Stores a user's rating for a book.
Fields
user:ForeignKey→Userbook:ForeignKey→Bookrating:PositiveSmallIntegerField(0–5)created_at:DateTimeFieldupdated_at:DateTimeField(auto_now=True)is_archived:BooleanField(defaultFalse) — hidden from profile/UI ifTruearchived_at:DateTimeField(nullable)
Meta
- Unique constraint on
(user, book)whereis_archived=Falseguarantees at most one active rating per user/book while allowing archived history.
Visibility
- Archived ratings are hidden from the UI but retained for analytics.
Represents a user's written review of a book.
Fields
user(FK):Userbook(FK):Bookcontent:TextFieldcreated_at:DateTimeFieldupdated_at:DateTimeField(auto_now=True)is_archived:BooleanField(defaultFalse)archived_at:DateTimeField(nullable)
Meta
- Unique constraint on
(user, book)whereis_archived=False.
Visibility
- Archived reviews are hidden from the UI but retained for analytics.
The NextChaptr project is divided into focused Django applications to ensure clear separation of concerns and maintainable architecture.
| App Name | Responsibility |
|---|---|
accounts |
Authentication for now; will hold profile features in the future |
books |
Google Books search/detail, minimal cached Book, admin, services, cover proxy endpoint (/cover/) |
activity |
Per-user ReadingStatus, Rating, Review persistence + admin |
library |
Displays user-specific reading activity with status information |
To avoid third-party cookies flagged by Lighthouse, cover images are served first-party:
- Endpoint:
GET /cover/<str:book_id> - Behavior: Server fetches the remote image (enforces HTTPS), whitelists Google hosts, and returns bytes with long-lived caching headers.
- Templates: Use
book.cover_urlor fall back to a local placeholder.
This affects Search Results, Book Detail, and Library templates and the corresponding views that compute cover_url for each book.
Lifecycle rules (e.g. “rating implies a status exists” or “removing status archives rating/review”) are implemented in a service layer. This makes behavior explicit, testable, and easy to evolve.
Key functions:
remove_from_library(user, book_id)— deletes the status and archives the user’s rating/review for that book.upsert_active_rating(user, book_id, value)— creates or unarchives the latest rating, ensuring a status exists.upsert_active_review(user, book_id, content)— creates or unarchives the latest review, ensuring a status exists.
- Modular design: Each app reflects a distinct domain of the system and aligns with a major feature group (search, authentication, interaction, monitoring).
- Separation of concerns: Each app encapsulates its own models, views, and templates.
- Maintainability: Clear boundaries reduce complexity and improve readability.
- Scalability: Supports future extensions (e.g. a social/friendship app) without disrupting core architecture.
Below is a summary of the planned development scope, organized into Agile epics, user stories, and technical tasks.
To maintain consistency and quality, dedicated templates were created for Epics, User Stories, Technical Tasks, and Bugs. These can be found in the Issue Template Folder.
All user stories follow the standard format:
As a user, I want action, so that I goal.
Each story also includes acceptance criteria, documented directly in its corresponding GitHub issue (linked under each Epic below). This ensures requirements are clear, testable, and traceable without duplicating content in the README
Epic 1: Book Discovery and Browsing
Goal: Enable users to explore the book catalog using a search interface powered by the Google Books API.
Technical Tasks
- Implement search form and view
- Integrate Google Books API
- Display search results
- Search automated tests
Technical Tasks
- Create book detail view
- Style Book Detail Page
- Populate data from API or local cache
- Book Detail Automated tests
Technical Tasks
Technical Tasks
Technical Tasks
- Implement homepage template and layout structure
- Add banner image placeholder and styling
- Implement short description section
- Display genre list with links to browse pages
- Apply responsive styling
- Home automated tests
Goal: Set up account registration, login/logout, and protect user actions.
Technical Tasks
- Create registration form and view
- Handle form validation and feedback
- Link registration in navbar
- Account register automated tests
Technical Tasks
- Create login and logout views
- Update navbar based on auth status
- Handle redirection after login/logout
- Log-in/Log-out automated tests
Technical Tasks
Goal: Allow users to track their reading activity, rate books, and comment.
Technical Tasks
- Create reading status and review models
- Add forms for status, rating, and reviewing
- Display and update user content
- Remove book Confirmation
- Reading Status Automated Tests
Technical Tasks
- Add rating field to reading model or separate model
- Create Rating Form or UI Control
- Show Average Book Rating
- Ensure rated books have reading status
- Remove rating
- Rate book automated tests
Technical Tasks
- Create review model and form
- Display reviews in template
- Review books always have status
- Automates Tests for leaving a review
Technical Tasks
- Validate review ownership
- Implement update and delete views for reviews
- Add conditional logic in templates for ownership
- Add messaging and UI confirmation for deletion
- Automated tests for editing and removing a review
Epic 4: User Library
Goal: Provide users with a personalized library to manage their reading activity.
Technical Tasks
- Create library view with user authentication
- Build styled library template
- Query and display grouped book data
- Automated tests for Library View
Technical Tasks
- Add inline status update controls
- Implement status update logic in view
- Show success messages after updates
NextChaptr includes a comprehensive suite of automated tests to ensure reliability and maintainability across core features. Tests are written using Django’s TestCase framework with mocking for external dependencies such as the Google Books API.
- Isolation: external API calls are mocked for speed and determinism.
- Resilience: cache is cleared between tests to avoid cross-test interference.
- Realism: sample JSON payloads (e.g.,
REALISTIC_DETAIL_JSON) simulate Google Books responses for reliable field mapping tests. - Coverage: Using
coverage.py, the test suite reports 92% overall line coverage across 94 test functions in 11 files. Core apps (accounts,books,activity,library) are fully exercised, ensuring authentication, search, interactions, and library logic are reliable. Only minor admin and utility paths remain uncovered.
Detailed testing documentation can be found in TEST.md.
The accounts app tests focus on the complete authentication flow, including signup, login, logout, and password reset. Tests validate correct page rendering, exclusion of unrelated UI elements (such as the search form), handling of validation errors (e.g. short passwords, mismatches, duplicate usernames), and successful account actions like login and confirmation email delivery. This ensures a reliable and secure authentication system for users.
The activity app has comprehensive tests around user interactions with books. Reading status tests confirm that only valid choices are allowed, redirects are handled securely, and unauthenticated users are properly blocked. Rating tests check that authenticated users can add, update, or remove ratings while ensuring statuses are created or respected. Review tests verify the full review lifecycle—creation, editing, deletion with ownership checks, and UI rendering. Archive flow tests ensure ratings and reviews are archived rather than lost when statuses are removed, and can be restored when new activity occurs. Delete action tests add coverage for safe deletion of reviews and library items from detail pages.
The books app tests cover all aspects of book discovery and display. Book detail tests ensure that metadata is rendered properly, pages include search functionality, and volumes are cached. Home view tests validate correct rendering of hero, about, and genre sections, template inheritance, and conditional UI for anonymous versus authenticated users. Search tests confirm that queries are built correctly with operators, API calls return expected results, errors are handled gracefully, and book fetching maps fields correctly. Pagination and genre browsing tests check that paging logic is consistent, filters are preserved, and genre links use correct URL structures, ensuring smooth navigation and discoverability.
The library app tests confirm that the personal library behaves consistently across different user states. For view tests, they ensure access control (login required), proper empty state messages, correct section rendering when books exist in specific statuses, and navigation links to book details. Action tests cover the ability to remove books, change statuses directly from the library, and display the correct links for writing reviews or ratings. Together, these tests validate both the backend logic and user-facing presentation of the personal library feature.
Run tests with:
python manage.py test --settings=chaptr.settings_testThese tests provide confidence that authentication flows and book-related features behave as expected.
A minimal Jest suite validates critical UI scripts, including debounced rating submissions, removeRating(), and delete confirmation modal behavior. Coverage reports show 68% statements, 46% branches, 75% functions, and 74% lines. These tests target the most important interactive features for user experience.
Tests are run with npm run test:js using jsdom.
Book Discovery & Search
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| Navigate genre tile | Click on "Classics" tile | Search /search/?field=subject&q=classics |
✅ |
| Search by title | Select "title" and type "Little Women" | Search /search/?field=title&q=Little+Women |
✅ |
| Search by author | Select "author" and type "John Grisham" | Search /search/?field=author&q=John+Grisham |
✅ |
| Search by genre | Select "genre" and type "political" | Search /search/?field=subject&q=political |
✅ |
| General search | Select "all" and type "Fellowship of the Ring" | Search /search/?field=all&q=Fellowship+of+the+Ring |
✅ |
| View book detail | Click on "The Fellowship of the Ring" | Page renders with author, description, publisher, pages, categories, publishing date | ✅ |
| Login to add book | Click on "Log in to add" | Redirects to accounts/login with /?next=/search |
✅ |
| Login to rate book | Click on stars to add a rating | Redirects to accounts/login with /?next=/search/ |
✅ |
| Login to review book | Click on "Log in" hyperlink | Redirects to accounts/login with ?next=/books/ |
✅ |
Authentication
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| Sign up | Add temp email, username & password | Redirect to /accounts/confirm-email/ and receive email |
✅ |
| Email verification | Click "verify your email" link → /accounts/confirm-email/ → confirm |
Alert You have confirmed {account}, then redirect to login |
✅ |
| Log in | Enter username and password | Display success message; redirect to /library/ |
✅ |
| Log out (menu) | Header icon → open menu → "Sign out" | Redirect to /accounts/logout/ |
✅ |
| Log out (button) | On /accounts/logout/ click "Sign out" |
Successfully signs out and shows alert | ✅ |
Library
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| First login | Enter username and password | Redirect to /library/ and show empty library message |
✅ |
| Sort titles | Library with multiple books/statuses → click "Status" header | Re-orders books by status (descending: To Read / Reading / Read) | ✅ |
| Filter titles | Library with multiple statuses → click "Read" filter | Shows only books with status "Read" | ✅ |
| Change status | Library → book with "To Read" → dropdown next to "View" → "Change Status" → select "Reading" | Updates status and shows confirmation | ✅ |
| Review book | Library → dropdown next to "View" → "Write a Review" | Redirects to book page #reviews |
✅ |
| Rate book | Library → dropdown next to "View" → "Rate" | Redirects to book page | ✅ |
| Remove book | Library → dropdown next to "View" → "Change Status" → "Remove from Library" | Confirmation modal shows with correct content | ✅ |
Review
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| Review form | Search title:jellyfish age backwards → first item /books/wYswEAAAQBAJ/ |
Sees reviews and form | ✅ |
| Leave review (no status) | On /books/wYswEAAAQBAJ/ write and submit “awesome” |
Page updates, alert shown, status becomes READ | ✅ |
| Edit review | On /books/wYswEAAAQBAJ/ click edit, change to “not so awesome”, save |
Review updated; alert shown | ✅ |
| Delete review | On /books/wYswEAAAQBAJ/ click delete |
Confirmation modal shown with correct messaging | ✅ |
| Cancel delete | Click "Cancel" | Review is not deleted | ✅ |
| Confirm delete | Click "Yes, proceed" | Review deleted; success alert shown | ✅ |
Rating
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| See rating average | /books/wYswEAAAQBAJ/ |
Rating average displayed | ✅ |
| Rate book | /books/wYswEAAAQBAJ/ → click stars to rate 4 |
Average and count update; stars change to filled for the given rating; success alert | ✅ |
| Update rating | /books/wYswEAAAQBAJ/ → change to 3 |
Average updates; count remains; stars reflect new rating; success alert | ✅ |
| Delete rating | /books/wYswEAAAQBAJ/ → click the x |
Average and count update; stars change to the gray state; info alert | ✅ |
Status Update
| Test Case | Input | Expected Outcome | Status |
|---|---|---|---|
| Set status on search | Search “old man and the sea” → click “To Read” button on a result item | Status updated; success alert shown | ✅ |
| Change status on search | On the same item, change from “To Read” to “Reading” | Status updated; success alert shown | ✅ |
| Remove status on search | On the same item, remove from library | Confirmation modal shown with correct content | ✅ |
| Cancel status removal | In the confirmation modal, click “Cancel” | Item retains its status | ✅ |
| Confirm status removal | In the confirmation modal, click “Yes, proceed” | Modal closes; status cleared | ✅ |
500 Error in signup
Mandatory confirmation email was not being sent due to issues with Gmail credentials. Resolved by regenerating the credentials and updating both .env and Heroku config vars.
Admin search for Activity gives 500 error
Admin search returned a 500 error. Resolved by correcting search field formatting.
Internal Server Error
Production returned a 500 error due to missing variables after refactoring. Resolved by adding GOOGLE_BOOKS_SEARCH_URL and GOOGLE_BOOKS_VOLUME_URL to Heroku config vars.
Books tests_views failing after activity changes
Book views crashed in tests because RequestFactory requests lack request.user, causing AttributeError in book_search/book_detail. Fixed by defaulting to AnonymousUser (e.g. user = getattr(request, "user", AnonymousUser())) before any is_authenticated checks in books/views.py.
API response for totalItems is inconsistent
Pagination broke due to Google Books API returning inflated totalItems. Fixed by capping results to the fetched items and adjusting pagination logic.
Message "No reviews yet" after reviews
Authenticated users saw “No reviews yet” after their own review due to template logic. Fixed by rendering the user’s review first and only showing the empty state when neither my_review nor any reviews exist.
Cover fallback not displaying
Cover fallback failed in production. Resolved by removing static files from .gitignore and correcting directory paths.
Notifications make the screen jump down
Alerts caused layout shift. Fixed by converting them to fixed-position toasts (high z-index), so messages float without pushing content.
Login via search results redirects to empty search
Login from search redirected to an empty results page because the next parameter wasn’t preserved. Fixed by passing the original query in the login redirect.
Models inconsistency
Removing a book only cleared its reading status, leaving review/rating behind. Fixed by cascading cleanup on status removal and archiving associated review and rating records.
Search returns duplicated results
Search results showed duplicates when the query was too specific. Fixed by de-duplicating results before pagination.
Rating info in Search Results but not in Book Page
Rating average and number of ratings were being passed on book_search view, but not on book_detail. That was causing an inconsistent behavior. Fixed by fetching the average and total number of ratings and passing in book_detail.
| Page | Warning / Error | Fix |
|---|---|---|
| Home | Error: Parse Error. </body>↩</html>↩ |
Removed extra whitespace after </html> in base.html |
| Home | The navigation role is unnecessary for nav |
Removed from base.html |
| Home | The region role is unnecessary for section |
Removed from home.html |
| Home | The contentinfo role is unnecessary for footer |
Removed from base.html |
| Search Results | End tag h2 seen, but there were open elements |
Corrected </h2> to </h1> |
| Search Results | sizes must only be specified if srcset is specified |
Removed sizes |
| Search Results | End tag h5 seen, but there were open elements |
Corrected </h5> to </h1> |
| Search Results | Duplicated book id error | De-duplicated search output in books/views.py |
| Book Detail | Error: Unclosed div |
Closed the div |
| Book Detail | Parse error | Wrapped `{{ book.description |
| Book Detail | Duplicate ID confirmDeleteModal |
Removed included modal partial from reviews.html |
| Line | Warning | Fix |
|---|---|---|
| 183 | background-color: none is not a valid value |
Changed to transparent |
| 766 | background-color: color-mix(...) not valid here |
Changed to transparent |
| 549, 564 | The clip property is deprecated |
Removed clip; kept clip-path |
Lighthouse
| Page | Warning / Issue | Fix |
|---|---|---|
| Home | Deprecation/Warning: <h1> inside an article/aside/nav/section without explicit font-size |
Added explicit font-sizes in CSS |
| Search Results & Book Detail | Mixed content: some resources loaded over HTTP while the page is HTTPS | Introduced ensure_https to secure Google Books cover URLs |
| Search Results & Book Detail | Third-party cookies via Google cover images | Implemented a /cover/ proxy view and URL to serve images first-party |
All files passed PEP8 validation. Screenshots can be found in the PEP8 folder.
| Page | Warning / Issue | |
|---|---|---|
| Home | Genre tiles aren't highlighted on keyboard navigation | Added outline on .genre-card and removed from .streched-link |
| All | Font icons present accessibility limitations | Replaced font-awesome icons with SVG icons |
| Search | for and id values on search form didn't match |
Ensure the values matched |
| Search | Too low contrast on button with accent color | Changed accent color to a deeper orange tone |
| Search | Links need a visual indication other than color | Added text-decoration: underline to titles on search results |
| Search | Heading missing levels | Applied headings according to hierarchy and used bootstrap classes to keep style according to desired design |
| Log-in/Sign-up | Too low contrast on links using secondary color | Added a secondary-color-dark to use as links font-color |
| Book Details & Search | Rating component doesn't work via keyboard | Grouped radio buttons in <fieldset>, ensured addEventListener("change") and removeRating() could be triggered by keyboard in rating.js |
| Book Details & Search | Emtpy ratings constrast ratio too low | Changed the stars color to darker shade of gray |
| Book Details | Review buttons have no labels | Added title and aria-label |
| Book Details | Missing aria-label on form, design uses only placeholder | Added aria-label to form |
Sprints deliver features incrementally, each focusing on user stories. The sprint timebox is one week.
Planning was done based on User Story level, using Fibonacci sequence scale (1, 2, 3, 5, 8, 13…) to measure effort.
- 1 → trivial, small fix or UI tweak
- 2 → simple story, one component or view, limited logic
- 3 → moderate story, some backend + template work, maybe 1–2 test cases
- 5 → complex story, multiple moving parts, integration with API or model changes
- 8 → large/uncertain story, touching multiple apps, several acceptance criteria
All issues are tracked on the GitHub project board:
https://github.com/larevolucia/chaptr/projects/12
- Create epics, user stories, and tasks in GitHub
- Set up Django project and apps
- Configure Google Books API integration
- Deploy initial version to Heroku
- Set up Postgres database
- Set up basic templates and static files
Sprint 1 — Total: 16 points · Should-have: 3 points (19%)
-
Epic 1: Book Discovery and Browsing
- [MUST] Search for books by title, author, genre — 5 pts
- [MUST] View book details — 3 pts
- [SHOULD] Homepage – MVP — 3 pts
-
Epic 2: User Authentication and Permissions
- [MUST] Register an account — 3 pts
- [MUST] Log in and log out securely — 2 pts
Sprint 2 — Total: 18 points · Should-have: 3 points (17%)
-
Epic 1: Book Discovery and Browsing
- [SHOULD] Prompt login when guests try to interact — 1 pt
- [SHOULD] Homepage – Stretch — 1 pt
-
Epic 2: User Authentication and Permissions
- [SHOULD] Restrict book interactions to authenticated users — 1 pt
-
Epic 3: Book Interaction and Reading Progress
- [MUST] Mark books as To Read, Reading, or Read — 5 pts
- [MUST] Rate books — 5 pts
- [MUST] Leave a review — 5 pts
Sprint 3 — Total: 13 points · Should-have: 2 points (15%)
-
Epic 1: Book Discovery and Browsing
- [SHOULD] View reviews on books — 2 pts
-
Epic 3: Book Interaction and Reading Progress
- [MUST] Edit and delete reviews — 3 pts
-
Epic 4: User Dashboard/Library
- [MUST] View books grouped by reading status — 5 pts
- [MUST] Update reading status directly from dashboard — 3 pts
Sprint 4 — Testing & Documentation (no points assigned)
- [MUST] Accessibility & Performance
- [MUST] Documentation
- [COULD] Refactoring
git clone https://github.com/larevolucia/chaptr.git
cd chaptrVerify your Python version:
python3 --versionNote: This project requires Python 3.12+ (see
.python-version).
python3 -m venv venv
# Activate:
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activatepip install -r requirements.txtCreate a .env file in the project root with the following variables:
- Go to the Google Cloud Console.
- Create a new project (guide).
- Enable the Google Books API (
APIs & Services > Library). - Generate an API key (
APIs & Services > Credentials).
Add the key and URLs to .env:
GOOGLE_BOOKS_API_KEY=<YOUR_KEY>
GOOGLE_BOOKS_SEARCH_URL=https://www.googleapis.com/books/v1/volumes
GOOGLE_BOOKS_VOLUME_URL=https://www.googleapis.com/books/v1/volumes/{}SECRET_KEY=<YOUR_DJANGO_SECRET_KEY>
DATABASE_URL=<YOUR_DATABASE_URL>For local testing you can disable email confirmation in settings.py:
ACCOUNT_EMAIL_VERIFICATION = "none"For production email (example with Gmail App Password):
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=<YOUR_EMAIL_ADDRESS>
EMAIL_HOST_PASSWORD=<YOUR_APP_PASSWORD>
DEFAULT_FROM_EMAIL=<YOUR_EMAIL_ADDRESS>Before deploying (using WhiteNoise):
python manage.py collectstatic --noinputDeployment follows Django’s recommended production hardening:
DEBUG = FalseALLOWED_HOSTSset to production domainsCSRF_COOKIE_SECURE = TrueSESSION_COOKIE_SECURE = TrueSECURE_HSTS_SECONDS = 31536000(HSTS enabled for HTTPS only)- All secrets stored in environment variables (
.env/ Heroku Config Vars) - No secrets or passwords committed to GitHub
- Create a new Heroku app.
- Add the Heroku Python buildpack under Settings > Buildpacks.
- Add environment variables from
.envto Heroku Config Vars. - Deploy.
That’s it! The app should now be live on Heroku.
- Homepage banner image: Unsplash — Photo by Lilly Rum
- Google Books API: Google Developers
- Color palette: Material Palette
- Favicon: Favicon.io
- Favicon art by Good Ware
- ChatGPT: OpenAI for documentation improvement
- Copilot: GitHub Copilot for code completion and docstring generation
- Django Allauth: Django Allauth
- Django Forum: Django Project
- Conditional Requests: MDN Web Docs
- Session Objects: Requests Documentation
- Third-party Cookies: Privacy Sandbox
- Custom error handling: Stack Overflow
- Coverage: Coverage
- Jest: Jest
- Geeks For Geeks : Jest Testing
- Mozilla Org: Testing and More
Many thanks to my mentor, who always pushed me a little further,especially when I felt there was nothing more I could do. I’m also grateful to my colleagues, who generously offered their professional advice and guidance throughout this journey.























































