-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Developing the Reading Log
This is a tutorial on contributing to the "My Books" experience and developing the Reading Log.
For information on using the My Books Dropper, go here.
When a patron clicks on their My Book
main navigation button, they're brought to the url /account/books
and shown My Books
overview that provides a summary of their loans, lists, reading log, and more:
The html template that renders the My Books
experiences is called books.html. This base template is used to render a variety of different my books pages consists of a left sidebar menu that looks like this:
The books.html
template also contains rules for deciding what primary content and sub-templates will be rendered to the right-side of the menu, e.g.:
The main books.html
base template is used to render the patron's...
- My Books Overview page (shown above)
- Loans page
- Reading Log pages -- example
- Lists overview page
- Individual List pages
- Loan History page (in development but will look similar to the Reading Log pages
As explained in the Patron's Experience section above, when a patron clicks on their My Books button, they will be brought to /account/books
which will then redirect to /people/{openlibrary_username}/books
. The routers that handles these redirects is defined in https://github.com/internetarchive/openlibrary/blob/35d6b72c7851f260d673fbcd9ce3f95b0e9c3169/openlibrary/plugins/upstream/account.py#L792-L811. These redirects route to another set of routes defined in the mybooks.py plugin: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/mybooks.py#L23-L44. These routers essentially look for any url pattern of the form /people/{openlibrary_username}/{path}
and uses a controller in the same file, a class called MyBooksTemplate
(defined on https://github.com/internetarchive/openlibrary/blob/35d6b72c7851f260d673fbcd9ce3f95b0e9c3169/openlibrary/plugins/upstream/mybooks.py#L184-L368) to render the appropriate my books view (such as overview, loans, loan history, reading log, lists, an individual list, etc).
Depending on the {path}
specified within the url, different models may be used to prepare and fetch data and different sub-templates will ultimately be within the books.html
base template. Within the MyBooksTemplate
, a variable named key
is used to make determinations based on the {path}
about what data to fetch and what sub-templates to render .
- Patron types in the url
/account/books
after logging in, which is matched by the route inplugins/account.py
- Patron is redirected to
/people/{their_openlibrary_username}/books
which is matched by themy_books_home
ormy_books_view
routes inplugins/mybook.py
on https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/mybooks.py#L23 - The router calls the
MyBooksTemplate
controller with a key ofmybooks
(instructing it to fetch the corresponding data for, and to render, themybooks
view): https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/mybooks.py#L184 - In the preamble / initialization of the
MyBooksTemplate
controller, shared data will be fetched that is required by every view to generate the left sidebar menu (such as the logged in patron, the names of their lists, reading log counts): https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/mybooks.py#L202-L237 - Next, different
if
statements and control structures are hit to determine which data we should load and send totemplates/books.html
based on thekey
derived from the url pattern (e.g.mybooks
,lists
,loan_history
, etc). - In this example, we determine based on the url we're on and the
key
value specified, that the LoggedBooksData model should be used to fetch the book data we need to render our view: https://github.com/internetarchive/openlibrary/blob/35d6b72c7851f260d673fbcd9ce3f95b0e9c3169/openlibrary/plugins/upstream/mybooks.py#L254-L270 - Eventually, when all the appropriate data is collected, it will be passed into the books.html base template: https://github.com/internetarchive/openlibrary/blob/35d6b72c7851f260d673fbcd9ce3f95b0e9c3169/openlibrary/plugins/upstream/mybooks.py#L288-L303
- In
books.html
, logic is defined that dynamically determines what the title of the web page should be (https://github.com/internetarchive/openlibrary/blob/35d6b72c7851f260d673fbcd9ce3f95b0e9c3169/openlibrary/templates/account/books.html#L27-L54), renders the left sidebar (https://github.com/internetarchive/openlibrary/blob/master/openlibrary/templates/account/books.html#L74) and then renders the correct child template for this view: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/templates/account/books.html#L154-L173
- A new router defining your desired url pattern needs to be defined in
plugins/mybooks.html
, such as is done bymy_books_view
. This router should make a call toMyBooksTemplate().render()
-
MyBooksTemplate
should be updated so there is an additional check within theif
/elif
control flow for the newkey
or page you wish to add. This section should fetch the books or data which will be required by the view. - You'll want to update
templates/book.html
(the base template for all ofMy Books
views) so that it defines what the title should be and what templates should be rendered when your url pattern /key
is encountered. - You may then have to create a new template within
templates/account/
that renders the data in a suitable way. In many cases, we'll want to refer to theaccount/reading_log.html
template as the basis for our design: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/templates/account/reading_log.html
For a complete, minimal example of adding a new page or view to the My Books system, please refer to PR #8375 as well as this comment which describes how data specific to a new loan_history
page may be prepared that is suitable to be passed through the account/books.html
base template.
NOTE: This section is now outdated as much of the routing and controller logic has been moved out of plugins/upstream/account.py
and in to plugins/upstream/mybooks.py
, into the MyBooksTemplate
. Still, this section is useful to see how we went about extending the functionality of the Reading Log page to add the ability to search for books on your currently reading, want to read, and already reading shelves.
In #5080, you can read through a slightly unrealistic example of adding Search filtering capabilities to the Reading Log:
By unrealistic, we mean that this proposal currently will not work as implemented because book titles, authors, and the other data we'd like to search for are not kept in our ReadingLog db table, only the OL identifiers. We may be able to achieve this with solr in the future. But assuming we did have the desired info in our database (and as a thought exercise):
- First, we'd need to update the Reading Log html template (https://github.com/internetarchive/openlibrary/blob/master/openlibrary/templates/account/books.html) to include a search box (design task). For a first version, we'd probably use an html form which submits a GET search query , similar to what we have on the author's page: https://openlibrary.org/authors/OL7283091A . In the future, we might want to use javascript (similar to how we the real-time Search box works at the top of the website):
- Next, we'd need to update the
public_my_books
controller method in https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/account.py#L733-L760 to accept a GET parameter. Already, the function expects apage
variable to be sent as a GET parameters (https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/account.py#L738) so accomplishing this should be as straightforward as adding another parameters like,i = web.input(page=1, search=None)
. - When/where we fetch the patron's books here: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/plugins/upstream/account.py#L754, we need to alter the logic to check whether a
i.search
query is present (e.g.if i.search
). If thei.search
value is present, we'll need change the linereadlog.get_works
call so this optionalsearch
parameter is passed along with our request for matching books. -
readlog
is an instance ofplugins.upstream.account.ReadingLog
(class defined here: https://github.com/internetarchive/openlibrary/blob/1f57759886b65430d805270830677120c1dc067d/openlibrary/plugins/upstream/account.py#L645). Itsget_works
method (https://github.com/internetarchive/openlibrary/blob/1f57759886b65430d805270830677120c1dc067d/openlibrary/plugins/upstream/account.py#L716) will need to be updated to accept an optionalsearch
parameter (e.g.(key, page=1, limit=RESULTS_PER_PAGE, search=None)
). ThisReadingLog.get_works
function essentially uses aKEYS
dictionary (defined here: https://github.com/internetarchive/openlibrary/blob/1f57759886b65430d805270830677120c1dc067d/openlibrary/plugins/upstream/account.py#L654-L660) to lookup and then invoke the proper book-fetching function. - Each of the corresponding
ReadingLog
methods referenced by theKEYS
dictionary (namely:get_waitlisted_editions
,get_loans
,get_want_to_read
,get_currently_reading
,get_already_read
) must thus also be updated to take an optionalsearch
parameter. Each of these functions ultimately makes an API call to the same function within ourBookshelves
API model:Bookshelves.get_users_logged_books
(https://github.com/internetarchive/openlibrary/blob/master/openlibrary/core/bookshelves.py#L118-L149) - After a search box form has been added to the
template
, thepublic_my_books
view/controller has been edited to expect asearch
parameter, thissearch
parameter is forwarded to ourreadlog.get_works
call, and thereadlog
object (i.e. theReadingLog
class) have all been updated to accept an optionalsearch
parameter, we'll then need to do the hard work of modifying the actual APIBookshelves.get_users_logged_books
(the thing which calls the database) to consider the possibility of an optional search parameter when requesting data from the database: https://github.com/internetarchive/openlibrary/blob/master/openlibrary/core/bookshelves.py#L118-L149).
The same example above (which pretends to add Search filtering to the Reading Log) can be adapted to add an option to sort one's Reading Log entries by date added, such as is requested in Issue #4267.
Getting Started & Contributing
- Setting up your developer environment
- Using
git
in Open Library - Finding good
First Issues
- Code Recipes
- Testing Your Code, Debugging & Performance Profiling
- Loading Production Site Data ↦ Dev Instance
- Submitting good Pull Requests
- Asking Questions on Gitter Chat
- Joining the Community Slack
- Attending Weekly Community Calls @ 9a PT
- Applying to Google Summer of Code & Fellowship Opportunities
Developer Resources
- FAQs: Frequently Asked Questions
- Front-end Guide: JS, CSS, HTML
- Internationalization
- Infogami & Data Model
- Solr Search Engine Manual
- Imports
- BookWorm / Affiliate Server
- Writing Bots
Developer Guides
- Developing the My Books & Reading Log
- Developing the Books page
- Understanding the "Read" Button
- Using cache
- Creating and Logging into New Users
- Feature Flagging
Other Portals
- Design
- Librarianship
- Communications
- Staff (internal)
Legacy
Old Getting Started
Orphaned Editions Planning
Canonical Books Page