Skip to content

Instantly share code, notes, and snippets.

@chrisvogt
Last active June 13, 2019 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisvogt/7033c317c1038eaa4e0b270f8dd916c6 to your computer and use it in GitHub Desktop.
Save chrisvogt/7033c317c1038eaa4e0b270f8dd916c6 to your computer and use it in GitHub Desktop.
These are my notes for a project to render book-reading status updates on my personal website.

Goodreads status updates

Configure node-xml2js

Current version: 0.2 Package: https://www.npmjs.com/package/xml2js Documentation: https://github.com/Leonidas-from-XIV/node-xml2js

The XML parser can be configured to do many, if not all, of the transformations needed to format the JSON response data as I'd like.

⚠️ The content below is a rough draft

Extract from user.show response

DATA.GoodreadsResponse.user[0].updates[0].update is an array of objects.

Types

Following are the user updates I am interested in for this project.

OBJECT.$.type = (string)
  - review (e.g., 'gave 5 stars to...)
  - userstatus (e.g., 'is on page 841 of 896...'),
  - readstatus (e.g., 'is currently reading...')

Response => Transformed

These sections outline the data I intend to target. The response schema is still being determined.

Common Object / Properties
type DefaultOptionalString = string | ''; // undecided if I should do this
interface Object {
  type: string;
  [propName: string]: any;
}
interface Actor {
  name: string;
  goodreadsID: number;
  imageURL?: string;
  link: string;
}
interface Author {
  about?: string;
  averageRating: number;
  goodreadsID: number;
  hasImage: boolean = false;
  hasSmallImage: boolean = false;
  imageURL: DefaultOptionalString;
  link: DefaultOptionalString;
  name: string;
  ratingsCount: number;
  smallImageURL: DefaultOptionalString;
  textReviewsCount: number;
}
interface Book {
  goodreadsID: number;
  isbn?: string;
  isbn13?: string;
  title: string;
  sortTitle: string;
  pageCount: number;
  publisher?: string;
  languageCode?: string;
  author: Author
}

⚠️ The types defined below will be refactored into common update types

Review

Issued when a user completes reading a book and gives it a rating.

gave 5 stars to <a href="https://www.goodreads.com/book/show/35429993-fall-or-dodge-in-hell\">Fall, or Dodge in Hell (ebook) by <a href="https://www.goodreads.com/author/show/545.Neal_Stephenson\">Neal Stephenson

interface Update {
  type: string = 'review';
  actionText: string;
  actor: Actor;
  link?: string;
  object: {
    type: string = 'book';

  }
}


/*
review: (if REVIEW.object[0].book exists) {
  actionText: REVIEW.action_text[0] (string),
  actor: REVIEW.actor[0] (actor),
  link: REVIEW.link[0] (string),
  object: REVIEW.object[0].book (object) {
    type: 'book',
    goodreadsID: BOOK.id[0],
    title: BOOK.title[0],
    link: BOOK.link[0],
    author: BOOK.authors[0].author[] (array of object)
  },
  imageURL: REVIEW.image_url[0] (string),
  updatedAt: REVIEW.updated_at[0] (string),
}
User Status

Issued when a user updates their pages for a book.

Example: is on page 841 of 896 of <a href="https://www.goodreads.com/book/show/35429993-fall-or-dodge-in-hell\">Fall, or Dodge in Hell

status:  {
  actionText: STATUS.action_text[0],
  link: STATUS.link[0],
  imageURL: STATUS.image_url[0],
  actor: STATUS.actor[0] (actor),
  updatedAt: STATUS.updated_at[0] (input: datetime, output: datetime),
  object: STATUS.object[0].user_status[0] {
    type: 'book',
    goodreadsID: OBJECT.id[0]._ (input: string, output: integer),
    userID: OBJECT.user_id[0]._ (input: string, output: integer),
    page: OBJECT.page[0]._ (input: string, output: integer),
    percent: OBJECT.percent[0]._ (input: string, output: integer),
    createdAt: OBJECT.created_at[0]._ (input: datetime string, output: datetime),
    updatedAt: OBJECT.updated_at[0]._ (input: datetime string, output: datetime),
    book: OBJECT.book[0] (object) {
      goodreadsID: BOOK.id[0]._ (input: string, output: integer),
      isbn: BOOK.isbn[0] (string),
      isbn13: BOOK.isbn13[0] (string),
      title: BOOK.title[0] (string),
      sortTitle: BOOK.sort_by_title[0],
      pageCount: BOOK.num_pages[0]._ (input: string, output: integer),
      publisher: BOOK.publisher[0],
      languageCode: BOOK.lang_code[0],
      author: BOOK.author[multiple or 0] (object) {
        name: AUTHOR.name[0],
        about: AUTHOR.about[0] (string short bio),
        sortName: AUTHOR.sort_by_name[0] (string),
        shelfName: AUTHOR.shelf_display_name[0],
      }
    }
  }
}
Read Status

Issued when a user begins reading a new book.

is currently reading <a only_path="false" href="https://www.goodreads.com/review/show/2847236165\">Fall, or Dodge in Hell

status: {
  actionText: STATUS.action_text[0],
  link: STATUS.link[0],
  image_url: STATUS.image_url[0],
  actor: STATUS.actor[0] (actor),
  updatedAt: STATUS.updated_at[0] (datetime),
  object: OBJECT[0].read_status[0] {
    goodreadsID: READSTATUS.id[0]._ (input: string, output: integer),
    type: 'readStatus',
    status: READSTATUS.status[0] (string, example: 'currently-reading'),
    updatedAt: READSTATUS.updated_at[0]._ (input: datetime string, output: datetime),
    book: READSTATUS.book[0] (object) {
      goodreadsID: BOOK.id[0]._ (input: string, output: integer),
      isbn: BOOK.isbn[0] (string),
      isbn13: BOOK.isbn13[0] (string),
      title: BOOK.title[0] (string),
      sortTitle: BOOK.sort_by_title[0],
      pageCount: BOOK.num_pages[0]._ (input: string, output: integer),
      publisher: BOOK.publisher[0],
      languageCode: BOOK.lang_code[0],
      author: BOOK.author[multiple or 0] (object) {
        name: AUTHOR.name[0],
        about: AUTHOR.about[0] (string short bio),
        sortName: AUTHOR.sort_by_name[0] (string),
        shelfName: AUTHOR.shelf_display_name[0],
      }
    }
  }
}

Notes

Any data grabbed via ._ can have the type verified by checking the (optional) corresponding .$.type object.

$.type = integer
$.type = datetime
$.nil === true (means data does not exist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment