diff --git a/docs-src/docs/narratives/create-pdf.md b/docs-src/docs/narratives/create-pdf.md new file mode 100644 index 000000000..a7b430920 --- /dev/null +++ b/docs-src/docs/narratives/create-pdf.md @@ -0,0 +1,63 @@ +--- +title: "Converting a narrative to PDF" +--- + +This documentation will walk through how to turn a narrative into a static PDF. +We understand that there are multiple reasons why PDFs may be a necessary method of distribution, but stress that narratives are designed to be viewed through auspice where you have the ability to interact with and interrogate the data. + +If you run into any bugs, please [get in contact with us (email)](mailto:hello@nextstrain.org). + +## Prerequisites + +This requires [Decktape](https://github.com/astefanutti/decktape) to be installed, which itself requires Node.js. +If you've followed the [auspice install instructions](../introduction/install) and are using a conda environment then this is straightforward. +If not, you can create the necessary conda environment via: + +```bash +conda create --name auspice nodejs=10 +``` + +Install Decktape via: +```bash +conda activate auspice # or whichever conda environment has nodejs +npm install --global decktape +``` + + +## Saving an online narrative to PDF + +Let's say we want to save the example Genomic-Epi Situation Report for Ebola in the DRC available at [nextstrain.org/narratives/inrb-ebola-example-sit-rep](https://nextstrain.org/narratives/inrb-ebola-example-sit-rep) which you can see has 6 different "pages" of content. +We use decktape to access these different pages & save their output as PDF via: + +```bash +decktape generic --load-pause 3000 --key ArrowDown --size 1600x900 \ + https://nextstrain.org/narratives/inrb-ebola-example-sit-rep example.pdf +``` +You'll see a lot of bad-looking warning messages printed in the terminal 😱, but as long as you see "Printed 7 slides" or similar then the command has worked. +You should now be able to view a static PDF of the narrative, in this case at `example.pdf`. +Feel free to play around with the different options available to decktape (run `decktape generic --help` to see them). + +> Note that this technique won't work if authentication is required. Please refer to the section on saving local narratives to PDF for how to do this. + +## Saving a local narrative to PDF + +If you have a local narrative then the concept is similar to saving an online narrative, except we need to start a auspice server locally to display our narrative & allow it to be accessed by decktape. +These instructions assume you have installed auspice into a conda enviornment as per the [auspice install instructions](../introduction/install). + +Start up auspice locally, sourcing your own datasets & narratives via: +```bash +conda activate auspice # or whichever conda environment has auspice installed +auspice view --datasetDir --narrativeDir +``` + +You should now be able to view your narrative at [localhost:4000](http://localhost:4000). +For the purposes of this guide, i'll assume that we've followed the [Writing a Narrative](how-to-write) guide, and thus have a narrative describing mumps in North America available at [localhost:4000/narratives/example](http://localhost:4000/narratives/example). + +In a separate terminal window, run +```bash +conda activate auspice # or whichever conda environment has dectape installed from earlier +decktape generic --load-pause 3000 --key ArrowDown --size 1600x900 \ + http://localhost:4000/narratives/example example.pdf +``` +You'll see a lot of bad-looking warning messages printed in the terminal 😱, but as long as you see "Printed 11 slides" or similar then the command has worked. +You should now be able to view a static PDF of the narrative, in this case at `example.pdf`. diff --git a/docs-src/docs/narratives/how-to-write.md b/docs-src/docs/narratives/how-to-write.md index bb3ef7952..32ee4f944 100644 --- a/docs-src/docs/narratives/how-to-write.md +++ b/docs-src/docs/narratives/how-to-write.md @@ -11,8 +11,7 @@ You can skip this step if you have generated your own dataset, but for the purpo ```bash mkdir datasets -curl http://data.nextstrain.org/mumps_na_meta.json --compressed -o datasets/mumps_na_meta.json -curl http://data.nextstrain.org/mumps_na_tree.json --compressed -o datasets/mumps_na_tree.json +curl http://data.nextstrain.org/mumps_na.json --compressed -o datasets/mumps_na.json ``` You should now be able to visualise the dataset (without any narrative functionality) via `auspice view --datasetDir datasets` diff --git a/docs-src/website/sidebars.json b/docs-src/website/sidebars.json index 3238aaa36..626b971b2 100644 --- a/docs-src/website/sidebars.json +++ b/docs-src/website/sidebars.json @@ -21,7 +21,8 @@ ], "Narratives": [ "narratives/introduction", - "narratives/how-to-write" + "narratives/how-to-write", + "narratives/create-pdf" ], "Release Notes": [ "releases/changelog", diff --git a/src/components/narrative/index.js b/src/components/narrative/index.js index 65668c737..bdcedcd6d 100644 --- a/src/components/narrative/index.js +++ b/src/components/narrative/index.js @@ -2,11 +2,11 @@ import React from "react"; import { connect } from "react-redux"; import queryString from "query-string"; +import Mousetrap from "mousetrap"; import { NarrativeStyles, linkStyles, OpacityFade } from './styles'; import ReactPageScroller from "./ReactPageScroller"; import { changePage } from "../../actions/navigation"; import { CHANGE_URL_QUERY_BUT_NOT_REDUX_STATE, TOGGLE_NARRATIVE } from "../../actions/types"; -import { sidebarColor } from "../../globalStyles"; import { narrativeNavBarHeight } from "../../util/globals"; /* regarding refs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components */ @@ -42,6 +42,14 @@ class Narrative extends React.Component { })); } }; + this.goToNextSlide = () => { + if (this.state.showingEndOfNarrativePage) return; // no-op + this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx+1); + }; + this.goToPreviousSlide = () => { + if (this.props.currentInFocusBlockIdx === 0) return; // no-op + this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx-1); + }; } componentDidMount() { if (window.twttr && window.twttr.ready) { @@ -52,6 +60,11 @@ class Narrative extends React.Component { if (this.props.currentInFocusBlockIdx !== 0) { this.reactPageScroller.goToPage(this.props.currentInFocusBlockIdx); } + /* bind arrow keys to move around in narrative */ + /* Note that "normal" page scrolling is not avaialble in narrative mode + and that scrolling the sidebar is associated with changing the narrative slide */ + Mousetrap.bind(['left', 'up'], this.goToPreviousSlide); + Mousetrap.bind(['right', 'down'], this.goToNextSlide); } renderChevron(pointUp) { const dims = {w: 30, h: 30}; @@ -63,15 +76,11 @@ class Narrative extends React.Component { }; if (pointUp) style.top = narrativeNavBarHeight + progressHeight; else style.bottom = 0; - let gotoIdx = pointUp ? this.props.currentInFocusBlockIdx-1 : this.props.currentInFocusBlockIdx+1; - if (this.state.showingEndOfNarrativePage) { - gotoIdx = this.props.blocks.length-1; - } const svgPathD = pointUp ? "M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z" : "M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"; return ( -
this.reactPageScroller.goToPage(gotoIdx)}> +
@@ -160,6 +169,7 @@ class Narrative extends React.Component { pathname: this.props.blocks[this.props.currentInFocusBlockIdx].dataset, query: queryString.parse(this.props.blocks[this.props.currentInFocusBlockIdx].url) }); + Mousetrap.unbind(['left', 'right', 'up', 'down']); } } export default Narrative;