Body → Chapter (required) Arabic numeral label descending (highest to lowest number), optionally including preceding label string text
Context
The context is provided in the Epic &2 and Table of contents building user stories in #1458 (closed).
NCBI did an analysis of their TOCs to find out what automatic ordering cases could be developed to reduce the need for users to manually order books. Each new ordering option is a separate issue linked below -- each to be developed in the priority order specified.
Proposal
Extend the options supported by including:
Book division | Ordering rule |
---|---|
Body → Chapter (required) | Arabic numeral label descending, optionally including preceding text specified by NCBI. Currently known values: Issue, Statistical Brief |
See Tab 2 of BCMS_TOC_Chapters_Sorting_Anaysis for details on the metadata nodes that are relevant to get the order values.
Sample content
Final Rendered TOC Example: https://www.ncbi.nlm.nih.gov/books/NBK425793/
Sample converted chapter XML file:
Ordering spec
XML element used to order content and target to write in TOC.XML
Syntax - {string_name} {Arabic number} where {string_name} is case insensitive and can be more than one word in string AND {Arabic number} may or may not be preceded by a # symbol
Note: the text or symbols preceding the number should not have to be specified.
Example:
<label>STATISTICAL BRIEF #542</label>
Target to write in TOC.XML is complete label in converted XML - see example:
<toc-entry>
<label>STATISTICAL BRIEF #541</label>
<title>Any Use and &#x201C;Frequent Use&#x201D; of Opioids among Elderly Adults in 2018&#x2013;2019, by Socioeconomic Characteristics</title>
Design
Current UI
The following currently exist in the organisation Book Templates for chapter-processed books, which gets applied to Book Settings.
Design amendment
There is no design amendment needed to support additional chapter ordering options. The new option will be listed in the current dropdown. The text for the dropdown is:
Document label – descending
Note: use en dash (–) not a hyphen (-)
Acceptance criteria
-
System Admin, Org Admin, and Editors will have the ability to automatically sort their chapters in the body of their book by a document label with an Arabic numeral in descending order to reduce the level of effort to manually manage the table of contents of large books across multiple paginated pages.
Settings
-
On the Book Settings page under the "Landing Page: Table of Contents" section, System Admin, Org Admin, and Editors will have the ability to select the drop down option under "Order Chapter by" to sort by "Document label - descending." -
The "Document label - descending" option will appear after the "Chapter label - ascending" option in the dropdown menu.
Book Manager Page
-
On the chapter-processed Book Manager dashboard page, users who have selected to automatically order their settings by "Document label - descending" will see all of their chapters that have successfully converted appear from the highest document label to the lowest document label. -
If the BCMS cannot identify a document label in the converted XML, a red TOC? indicator label will appear at the far right on the row of that chapter component to alert the user that it cannot be properly sorted for lack of information. -
The document label including any preceding label string text will appear before the chapter title of each chapter component exactly as it is tagged in the element in the converted book part metadata.
Bookshelf Table of Contents - THESE HAVE NOT BEEN MET, BUT AGREED TO MOVE THEM TO THE TOC XML MILESTONE AS PART OF #1391 and #1491
-
On the TOC page of the BCMS, users who have selected to automatically order their settings by "Document label - descending" and who have updated the published version of the TOC, will see all of their published chapters sorted from the highest document label to the lowest document label. -
The document label including any preceding label string text will appear before the chapter title of each chapter component exactly as it is tagged in the element in the converted book part metadata. -
The TOC.xml that the user downloads via the Download button on the bottom right of the TOC page and which is sent to NCBI as a chapter ingest job will be valid, meet all Bookshelf tagging guidelines and TOC XML building specification in #24 (closed), and display to the same level of quality as the Table of Contents managed when loaded to the live Bookshelf site from the Silverlight CMS.
Definition of ready
-
BCMS User Story / Context has been well defined -
The priority of the user story is specified and agreed -
Digital assets added (design, database scheme, mockups etc if relevant) -
Coko Technical Proposal approved by NCBI -
Testable Acceptance Criteria approved by NCBI -
Estimate of effort to complete (time or points) -
The issue has been broken down into development tasks (if necessary) -
Requirements Clarified -
The product owner and development team agree that the user story is ready for development -
NCBI adds “Dev_Ready”
Definition of done
-
All coding tasks are finished and implemented by Coko -
All unit tests are written and run by Coko -
QA approved by Coko -
Deployed and tested on “ncbidev” (by Coko team) -
Deployed and tested on “ncbi” (by NCBI team) -
NCBI approves that Acceptance Criteria Met
Implementation
Some background info for this to make sense :
We have two tables Divisions and BookComponents . A book has 3 divisions (Front , Body, Back) To each record there is an array field bookComponents that saves the id of the bookComponents that leave in the bookComponent table This is the field where we save the ordering of the bookComponents. The same happens for the BookComponents (Parts) that have other bookComponents under them, there is a field called bookComponents that keeps the order of the ids of other bookComponents (referenced to the same table).
So in our case this is the flow that is happening already:
- user opens the settings modal of a book and updates the order settings to the value he wants for example to
Arabic numeral label descending
. - That means will trigger an update to the settings of that Book which will in turns execute the the orderService functionality. (This works already nothing to change ) You can check that in this file : server/api/graphql/bookComponent/bookComponent.resolvers.js at line 524.
- By executing this Service an update will happen automatically for that specific Record (Model) and will update the list (in our above example that will update the bookComponents column of either the division or the bookComponent / part).
Order Service Overview
So For this issue to work we need to make use of the OrderService class. which is here server/services/orderService/orderService.js
. The orderService takes as arguments the model
(Book, BookComponent , etc), and based on the configuration we have implemented already in the Model does the ordering. So for example in the model BookComponent (server/models/bookComponent/bookComponent.js)
you will see a static function orderMappings
this takes care of the whole ordering . in That function we configure the identifiers of the Model the fields with the list we want to order (see above bookComponents field) and the field that should be checked for the ordering we want to do for each case (Arabic numeral, Title A-z, etc). Sometimes that field needs some manipulation before can be ordered and for that we have a custom function that we can run before the ordering, For example we want to order by Number 1,2,3, etc, but the field is in the following format: Part 1, Part 2, Part 3 etc.
This custom function is located in line 46 of the BookComponent Model (And this where we need to add the extra code for this feature to work).
What we need to add :
This the predicate function :
predicate: {
modelClass: Book,
idColumn: 'id',
foreignColumn: 'bookId',
field: book => {
const value = (book.settings.toc || {}).order_chapters_by || 'manual'
if (value === 'number')
return {
field: 'metadata.chapter_number',
type: 'Number',
value: v => (v ? v.replace(/\D/g, '') : 0),
}
if (value === 'title')
return {
field: 'title',
type: 'String',
value: v => {
return v === 'Untitled' ? null : v
},
}
return {
field: value,
type: 'String',
}
},
},
The value variable is the value that the user saves and wants to perform the ordering . So for example when the user saves the order as title
the service will trigger this function and will do the ordering based the return object
return {
field: 'title',
type: 'String',
value: v => {
return v === 'Untitled' ? null : v
},
}
and before performing the ordering will execute the value custom function that will normalize a little bit the values (example with Part 1, Part 2, etc.).
So all we need is to add at the field, the ordering cases that come from the ui (title, number) and return the field we want to compare for the ordering and the type.
Currently we support two Types at the OrderService 'String' , "Number" that will be enough for this issue.
Alternative approaches (if applicable)
Scheduling
- Milestone:
- Iteration:
- Dependencies: (list issue numbers if relevant)
- Development estimate (hours): 16 hours