diff --git a/package-lock.json b/package-lock.json index 72891b173..926b26e1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@keyv/sqlite": "^3.6.6", "@renovatebot/pep440": "^2.1.20", "@smithy/node-http-handler": "^2.1.8", + "@types/sanitize-html": "^2.13.0", "argparse": "^1.0.10", "aws-sdk": "^2.908.0", "chalk": "^2.4.1", @@ -43,7 +44,7 @@ "luxon": "^3.4.4", "make-fetch-happen": "^11.1.1", "mapbox-gl": "^3.2.0", - "marked": "^0.7.0", + "marked": "^14.1.3", "mime": "^2.5.2", "neat-csv": "^7.0.0", "negotiator": "^0.6.2", @@ -13055,6 +13056,15 @@ "@types/react": "*" } }, + "node_modules/@types/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==", + "license": "MIT", + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -23233,14 +23243,15 @@ } }, "node_modules/marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.3.tgz", + "integrity": "sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==", + "license": "MIT", "bin": { - "marked": "bin/marked" + "marked": "bin/marked.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 18" } }, "node_modules/media-typer": { @@ -39185,6 +39196,14 @@ "@types/react": "*" } }, + "@types/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==", + "requires": { + "htmlparser2": "^8.0.0" + } + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -46889,9 +46908,9 @@ } }, "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" + "version": "14.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.3.tgz", + "integrity": "sha512-ZibJqTULGlt9g5k4VMARAktMAjXoVnnr+Y3aCqW1oDftcV4BA3UmrBifzXoZyenHRk75csiPu9iwsTj4VNBT0g==" }, "media-typer": { "version": "0.3.0", diff --git a/package.json b/package.json index 901c06c59..6de3d7c74 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@keyv/sqlite": "^3.6.6", "@renovatebot/pep440": "^2.1.20", "@smithy/node-http-handler": "^2.1.8", + "@types/sanitize-html": "^2.13.0", "argparse": "^1.0.10", "aws-sdk": "^2.908.0", "chalk": "^2.4.1", @@ -63,7 +64,7 @@ "luxon": "^3.4.4", "make-fetch-happen": "^11.1.1", "mapbox-gl": "^3.2.0", - "marked": "^0.7.0", + "marked": "^14.1.3", "mime": "^2.5.2", "neat-csv": "^7.0.0", "negotiator": "^0.6.2", diff --git a/static-site/README.md b/static-site/README.md index 1d61f2e4d..0bf80694e 100644 --- a/static-site/README.md +++ b/static-site/README.md @@ -91,6 +91,26 @@ After the above PR is merged, the new team member can then be added to the [docs 2. Merge the PR and follow [instructions to release a new version of the theme on PyPI](https://github.com/nextstrain/sphinx-theme#releasing). 3. Once the new version is available on PyPI, trigger RTD rebuilds for the latest/stable doc versions to update the footer. +### Writing blog posts + +To author a new blog post, create a new file under `/static-site/content/blog/` following the existing file naming convention (`YYYY-MM-DD-the-title-here.md`, e.g., `2024-11-14-blog-posts-are-awesome.md`). The file should start with a block of YAML front matter, such as: + +``` yaml +--- +author: "James Hadfield" +date: "2018-05-14" +title: "New nextstrain.org website" +sidebarTitle: "New Nextstrain Website" +--- +``` + +followed by the content of the blog post, marked up using [Markdown](https://en.wikipedia.org/wiki/Markdown). Please observe the following conventions: + +* Images associated with blog posts should be placed in [`/static-site/public/blog/img/`](./public/blog/img) +* All images associated with a given blog post should start with a common filename prefix, which should be clearly related to the blog post; see the existing files in the directory for examples +* Image URLs in the post should be given in an origin-relative format; i.e., they should start with `/blog/img/` +* Links to other pages and resources on `nextstrain.org` should also be given in origin-relative form; i.e., they should NOT start with `https://nextstraing.org`, only with a `/` + ## Deploying The static documentation is automatically rebuilt every time the (parent) repo is pushed to master. diff --git a/static-site/app/blog/[id]/page.tsx b/static-site/app/blog/[id]/page.tsx new file mode 100644 index 000000000..bb8e77262 --- /dev/null +++ b/static-site/app/blog/[id]/page.tsx @@ -0,0 +1,126 @@ +import { Metadata } from "next"; +import { redirect } from "next/navigation"; +import React from "react"; + +import { BigSpacer } from "../../../components/spacers"; +import { + siteLogo, + siteTitle, + siteTitleAlt, + siteUrl, +} from "../../../data/BaseConfig"; + +import { getBlogPosts, markdownToHtml } from "../utils"; + +import styles from "./styles.module.css"; + +// just to avoid having to repeat this in a couple method sigs... +interface BlogPostParams { + id: string; +} + +// return a list of params that will get handed to this page at build +// time, to statically build out all the blog posts +export function generateStaticParams(): BlogPostParams[] { + return getBlogPosts().map((post) => { + return { id: post.blogUrlName }; + }); +} + +// generate opengraph and other metadata tags +export async function generateMetadata({ + params, +}: { + params: BlogPostParams; +}): Promise { + const { id } = params; + + // set up some defaults that are independent of the specific blog post + const baseUrl = new URL(siteUrl); + const metadata: Metadata = { + metadataBase: baseUrl, + openGraph: { + description: siteTitleAlt, + images: [ + { + url: `${siteUrl}${siteLogo}`, + }, + ], + siteName: siteTitle, + title: siteTitle, + type: "website", + url: baseUrl, + }, + }; + + // this is the specific post we're rendering + const blogPost = getBlogPosts().find((post) => post.blogUrlName === id); + + if (blogPost) { + const description = `Nextstrain blog post from ${blogPost.date}; author(s): ${blogPost.author}`; + + metadata.title = blogPost.title; + metadata.description = description; + metadata.openGraph!.description = description; + metadata.openGraph!.title = `${siteTitle}: ${blogPost.title}`; + metadata.openGraph!.url = `/blog/${blogPost.blogUrlName}`; + } + + return metadata; +} + +export default async function BlogPost({ + params, +}: { + params: BlogPostParams; +}): Promise { + const { id } = params; + + // we need this list to build the archive list in the sidebar + const allBlogPosts = getBlogPosts(); + + // and then this is the specific post we're rendering + const blogPost = allBlogPosts.find((post) => post.blogUrlName === id); + + // if for some reason we didn't find the post, 404 on out + if (!blogPost) { + redirect("/404"); + } + + const html = await markdownToHtml(blogPost.mdstring); + + return ( + <> + + +
+
+
+ +

{blogPost.title}

+

{blogPost.author}

+
+
+
+
+

Blog Archives

+ +
+
+
+ + ); +} diff --git a/static-site/app/blog/[id]/styles.module.css b/static-site/app/blog/[id]/styles.module.css new file mode 100644 index 000000000..42ce1572e --- /dev/null +++ b/static-site/app/blog/[id]/styles.module.css @@ -0,0 +1,71 @@ +.blogPostTitle { + clear: both; /* this is to let the title fall under the floated date, not under it */ + color: black; + font-size: 3.5rem; + font-weight: 400; + line-height: 40px; + text-align: left; + width: 100%; +} + +.blogPostAuthor { + color: black; + font-size: 2rem; + font-weight: 300; + margin: 1rem 0 2rem; +} + +.blogPostDate { + color: black; + float: right; + font-size: 1.4rem; + font-weight: 300; + min-height: 2rem; +} + +.blogPostBody { + color: black; + font-size: 1.6rem; + font-weight: 300; + line-height: var(--niceLineHeight); + margin-top: 0px; + padding-bottom: 25px; + width: 100%; +} + +.blogPostBody img { + max-width: 100%; +} + +.blogPostBody h1 { + color: black; + font-size: 3rem; + margin-top: 20px; + text-align: left; +} +.blogPostBody h2 { + font-size: 2.4rem; + font-weight: 300; + margin-top: 10px; +} +.blogPostBody h3 { + font-size: 1.8rem; + font-weight: 300; + margin-top: 10px; +} +.blogPostBody p { + margin-top: 10px; +} +.blogPostBody li { + margin-left: 3rem; +} + +.blogSidebar { + font-size: 14px; +} +.blogSidebar ul { + list-style: none; +} +.blogSidebar ul li { + margin: 1.2rem 0; +} diff --git a/static-site/app/blog/page.tsx b/static-site/app/blog/page.tsx new file mode 100644 index 000000000..a0693b884 --- /dev/null +++ b/static-site/app/blog/page.tsx @@ -0,0 +1,17 @@ +import { redirect } from "next/navigation"; + +import { getBlogPosts } from "./utils"; + +export default function Index(): void { + const mostRecentPost = getBlogPosts()[0]; + + // _technically_ getBlogPosts() could return an empty array and then + // mostRecentPost would be undefined -- to make the type checker + // happy, if for some reason mostRecentPost is undefined, we will + // detect that and redirect to the 404 page + const redirectTo = mostRecentPost + ? `/blog/${mostRecentPost.blogUrlName}` + : `/404`; + + redirect(redirectTo); +} diff --git a/static-site/app/blog/parseMarkdown.ts b/static-site/app/blog/parseMarkdown.ts new file mode 100644 index 000000000..67086df3e --- /dev/null +++ b/static-site/app/blog/parseMarkdown.ts @@ -0,0 +1,89 @@ +import { marked } from "marked"; +import sanitizeHtml, { Attributes, IOptions, Tag } from "sanitize-html"; + +import { siteUrl } from "../../data/BaseConfig"; + +export default async function parseMarkdown(mdString: string): Promise { + const rawDescription = await marked.parse(mdString); + + const sanitizerConfig: IOptions = { + allowedTags, // see below + allowedAttributes: { "*": allowedAttributes }, // see below + nonTextTags: ["style", "script", "textarea", "option"], + transformTags: { + a: transformA, // see below + }, + }; + + const cleanDescription: string = sanitizeHtml( + rawDescription, + sanitizerConfig, + ); + + return cleanDescription; +} + +function transformA(tagName: string, attribs: Attributes): Tag { + // small helper to keep things dry + const _setAttribs: (attribs: Attributes) => void = (attribs) => { + attribs.target = "_blank"; + attribs.rel = "noreferrer nofollow"; + }; + + const href = attribs.href; + if (href) { + const baseUrl = new URL(siteUrl); + try { // sometimes the `href` isn't a valid URL… + const linkUrl = new URL(href); + if (linkUrl.hostname !== baseUrl.hostname) { + _setAttribs(attribs); + } + } catch { + _setAttribs(attribs); + } + } + + return { tagName, attribs }; +} + +// All of these tags may not be necessary, this list was adopted from https://github.com/nextstrain/auspice/blob/master/src/util/parseMarkdown.js +const allowedTags = ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'em', 'strong', 'del', 'ol', 'ul', 'li', 'a', 'img']; +allowedTags.push('#text', 'code', 'pre', 'hr', 'table', 'thead', 'tbody', 'th', 'tr', 'td', 'sub', 'sup'); +// We want to support SVG elements, requiring the following tags (we exclude "foreignObject", "style" and "script") +allowedTags.push("svg", "altGlyph", "altGlyphDef", "altGlyphItem", "animate", "animateColor", "animateMotion", "animateTransform"); +allowedTags.push("circle", "clipPath", "color-profile", "cursor", "defs", "desc", "ellipse", "feBlend", "feColorMatrix", "feComponentTransfer"); +allowedTags.push("feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feFlood", "feFuncA"); +allowedTags.push("feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset"); +allowedTags.push("fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence", "filter", "font", "font-face"); +allowedTags.push("font-face-format", "font-face-name", "font-face-src", "font-face-uri", "g", "glyph", "glyphRef"); +allowedTags.push("hkern", "image", "line", "linearGradient", "marker", "mask", "metadata", "missing-glyph", "mpath", "path"); +allowedTags.push("pattern", "polygon", "polyline", "radialGradient", "rect", "set", "stop", "switch", "symbol"); +allowedTags.push("text", "textPath", "title", "tref", "tspan", "use", "view", "vkern"); + +const allowedAttributes = ['href', 'src', 'width', 'height', 'alt']; +// We add the following Attributes for SVG via https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute +// Certain values have been excluded here, e.g. "style" +allowedAttributes.push("accent-height", "accumulate", "additive", "alignment-baseline", "allowReorder", "alphabetic", "amplitude", "arabic-form", "ascent", "attributeName", "attributeType", "autoReverse", "azimuth"); +allowedAttributes.push("baseFrequency", "baseline-shift", "baseProfile", "bbox", "begin", "bias", "by"); +allowedAttributes.push("calcMode", "cap-height", "class", "clip", "clipPathUnits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cursor", "cx", "cy"); +allowedAttributes.push("d", "decelerate", "descent", "diffuseConstant", "direction", "display", "divisor", "dominant-baseline", "dur", "dx", "dy"); +allowedAttributes.push("edgeMode", "elevation", "enable-background", "end", "exponent", "externalResourcesRequired"); +allowedAttributes.push("fill", "fill-opacity", "fill-rule", "filter", "filterRes", "filterUnits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "format", "from", "fr", "fx", "fy"); +allowedAttributes.push("g1", "g2", "glyph-name", "glyph-orientation-horizontal", "glyph-orientation-vertical", "glyphRef", "gradientTransform", "gradientUnits"); +allowedAttributes.push("hanging", "height", "href", "hreflang", "horiz-adv-x", "horiz-origin-x"); +allowedAttributes.push("id", "ideographic", "image-rendering", "in", "in2", "intercept"); +allowedAttributes.push("k", "k1", "k2", "k3", "k4", "kernelMatrix", "kernelUnitLength", "kerning", "keyPoints", "keySplines", "keyTimes"); +allowedAttributes.push("lang", "lengthAdjust", "letter-spacing", "lighting-color", "limitingConeAngle", "local"); +allowedAttributes.push("marker-end", "marker-mid", "marker-start", "markerHeight", "markerUnits", "markerWidth", "mask", "maskContentUnits", "maskUnits", "mathematical", "max", "media", "method", "min", "mode"); +allowedAttributes.push("name", "numOctaves"); +allowedAttributes.push("offset", "opacity", "operator", "order", "orient", "orientation", "origin", "overflow", "overline-position", "overline-thickness"); +allowedAttributes.push("panose-1", "paint-order", "path", "pathLength", "patternContentUnits", "patternTransform", "patternUnits", "ping", "pointer-events", "points", "pointsAtX", "pointsAtY", "pointsAtZ", "preserveAlpha", "preserveAspectRatio", "primitiveUnits"); +allowedAttributes.push("r", "radius", "referrerPolicy", "refX", "refY", "rel", "rendering-intent", "repeatCount", "repeatDur", "requiredExtensions", "requiredFeatures", "restart", "result", "rotate", "rx", "ry"); +allowedAttributes.push("scale", "seed", "shape-rendering", "slope", "spacing", "specularConstant", "specularExponent", "speed", "spreadMethod", "startOffset", "stdDeviation", "stemh", "stemv", "stitchTiles", "stop-color", "stop-opacity", "strikethrough-position", "strikethrough-thickness", "string", "stroke", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "surfaceScale", "systemLanguage"); +allowedAttributes.push("tabindex", "tableValues", "target", "targetX", "targetY", "text-anchor", "text-decoration", "text-rendering", "textLength", "to", "transform", "type"); +allowedAttributes.push("u1", "u2", "underline-position", "underline-thickness", "unicode", "unicode-bidi", "unicode-range", "units-per-em"); +allowedAttributes.push("v-alphabetic", "v-hanging", "v-ideographic", "v-mathematical", "values", "vector-effect", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "viewBox", "viewTarget", "visibility"); +allowedAttributes.push("width", "widths", "word-spacing", "writing-mode"); +allowedAttributes.push("x", "x-height", "x1", "x2", "xChannelSelector"); +allowedAttributes.push("y", "y1", "y2", "yChannelSelector"); +allowedAttributes.push("z", "zoomAndPan"); diff --git a/static-site/app/blog/utils.ts b/static-site/app/blog/utils.ts new file mode 100644 index 000000000..c6a47de11 --- /dev/null +++ b/static-site/app/blog/utils.ts @@ -0,0 +1,72 @@ +import fs from "fs"; +import matter from "gray-matter"; +import path from "path"; +import { fileURLToPath } from "url"; + +import parseMarkdown from "./parseMarkdown"; + +export interface BlogPost { + author: string; + blogUrlName: string; + date: string; + mdstring: string; + sidebarTitle: string; + title: string; +} + +// Scans the ./static-site/content/blog directory for .md files and +// returns a chronologically-sorted array of posts, each with some +// basic metadata and the raw (unsanitized) markdown contents. +export function getBlogPosts(): BlogPost[] { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + + const postsDirectory = path.join(__dirname, "..", "..", "content", "blog"); + + const markdownFiles = fs + .readdirSync(postsDirectory) + .filter((fileName) => fileName.endsWith(".md")); + + const blogPosts: BlogPost[] = markdownFiles + .map((fileName): BlogPost | false => { + const { data: frontmatter, content: mdstring } = matter( + fs.readFileSync(path.join(postsDirectory, fileName), "utf8"), + ); + + // Our blog posts have frontmatter which includes author, date + // (YYYY-MM-DD format), post title, and an optional sidebar title. + // If the sidebar title isn't provided, the post title will be + // used for the sidebar title. + const { author, date, title } = frontmatter; + + if (!author || !date || !title) { + // console warning printed server-side + console.warn( + `Blog post ${fileName} skipped due to empty/incomplete frontmatter`, + ); + // we will filter these `false` values out momentarily + return false; + } else { + const blogUrlName = fileName.replace(/\.md$/, ""); + const sidebarTitle: string = frontmatter.sidebarTitle || title; + return { author, date, title, blogUrlName, sidebarTitle, mdstring }; + } + }) + // type guard to filter out false entries generated because of bad frontmatter + .filter((post: false | BlogPost): post is BlogPost => { + return !!post; + }) + // YYYY-MM-DD strings sort alphabetically + .sort((a, b) => (a.date > b.date ? -1 : 1)); + + return blogPosts; +} + +export async function markdownToHtml(mdString:string): Promise { + try { + return await parseMarkdown(mdString) + } + catch(error) { + console.error(`Error parsing markdown: ${error}`); + return '

There was an error parsing markdown content.

'; + } +} diff --git a/static-site/content/blog/2018-05-14-new-nextstrain-website.md b/static-site/content/blog/2018-05-14-new-nextstrain-website.md index 9b25fe96b..9dd93c280 100644 --- a/static-site/content/blog/2018-05-14-new-nextstrain-website.md +++ b/static-site/content/blog/2018-05-14-new-nextstrain-website.md @@ -22,7 +22,7 @@ This comprises the splash page and documentation (docs, about, blogs etc) all bu **Auspice:** is the visualisation app, build as a single page javascript application ([github](https://github.com/nextstrain/auspice)). -This is what used to be nextstrain.org, but is now only used to visualise datasets (e.g. [nextstrain.org/WNV](https://www.nextstrain.org/WNV)). +This is what used to be nextstrain.org, but is now only used to visualise datasets (e.g. [nextstrain.org/WNV](/WNV)). Removing the splash & about pages from auspice allows for easier development of auspice, and makes adding static content a matter of writing markdown rather than modifying the JavaScript of auspice. diff --git a/static-site/content/blog/2019-10-21-auspice-v2.md b/static-site/content/blog/2019-10-21-auspice-v2.md index 0d41c87a8..d5385461b 100644 --- a/static-site/content/blog/2019-10-21-auspice-v2.md +++ b/static-site/content/blog/2019-10-21-auspice-v2.md @@ -12,12 +12,12 @@ sidebarTitle: "Auspice v2" Read on for rationale behind this update and a detailed explanation of the changes. -![auspice-v2-gif](img/v2-pie-charts.gif) +![auspice-v2-gif](/blog/img/v2-pie-charts.gif) *Switching between colorings in Auspice v2* ## Backstory -Auspice started as the [nextstrain.org](https://nextstrain.org) visualisation tool, in fact for a long time nextstrain.org _was_ Auspice. +Auspice started as the [nextstrain.org](/) visualisation tool, in fact for a long time nextstrain.org _was_ Auspice. Slowly, we turned Auspice into a stand-alone tool and started expanding its capacity. This resulted in many v1.x releases (we made it all the way to v1.39.0), and things had a habit of changing a little too frequently, with not enough focus on documentation and communication. @@ -44,7 +44,7 @@ We used to use colour blending to represent all the different traits present at This worked pretty well for continuous traits (e.g. the dates of isolates in each country), but performed poorly for discrete traits (e.g. the different flu clades present in each country). We now use pie charts for discrete traits which give a much nicer summary of the data: -![pie-charts](img/v2-pie-charts.png) +![pie-charts](/blog/img/v2-pie-charts.png) *Notice the difference? Auspice v1 (left) & v2 (right)* @@ -55,7 +55,7 @@ Additionally the format of the input JSONs to Auspice changed a bit throughout t We've settled on a new single "v2" JSON, containing pretty similar information to the v1 JSONs but in a format which allows us to expand the functionality of Auspice. -> Don't panic - `auspice view` and [nextstrain.org](https://nextstrain.org) will automatically convert the "v1" JSONs into the new format for you so all of the old datasets will continue to work with Auspice v2. +> Don't panic - `auspice view` and [nextstrain.org](/) will automatically convert the "v1" JSONs into the new format for you so all of the old datasets will continue to work with Auspice v2. | file | schema | auspice versions | description | | ---- | ---- | ---- | ---- | @@ -131,7 +131,7 @@ For instance, v1 would use the aa-nt toggle in the entropy panel to decide which We now show both. -![more-tree-info](img/v2-tree-info.png) +![more-tree-info](/blog/img/v2-tree-info.png) *Auspice v1 (left) & v2 (right). v2 shows more information on both tree hover (upper panel) & when clicking on tips (lower panel).* @@ -155,7 +155,7 @@ We now (a) show dates on the tree's x-axis using months & days, depending on the These help with the interpretation of trees over smaller time scales. See [PR #804](https://github.com/nextstrain/auspice/pull/804). -![time-labels](img/v2-time-labels.png) +![time-labels](/blog/img/v2-time-labels.png) *Above: Auspice v1's decimal labels were somewhat hard to interpret. Below: v2 displays calendar dates as appropriate, and uses more intelligent grid spacing.* ## Map "reset zoom" button zooms to include all demes @@ -169,11 +169,11 @@ See [PR #802](https://github.com/nextstrain/auspice/pull/802). If your analysis produces results in `-` (gaps), `X` (unknown residue) or `N` (unknown nucelotide) then we now colour these grey, making it much easier to see when data is missing. See [PR #799](https://github.com/nextstrain/auspice/pull/799). -![base-colours](img/v2-base-colours.png) +![base-colours](/blog/img/v2-base-colours.png) *Same analysis, different colour schemes, different interpretation.* ## Removal of Twitter & Google Analytics -These were a holdover from the early days when [nextstrain.org](https://nextstrain.org) and Auspice were the same thing. +These were a holdover from the early days when [nextstrain.org](/) and Auspice were the same thing. We've now removed all calls to Twitter, and made Google Analytics opt in. See [requests made from the client](https://nextstrain.github.io/auspice/customise-client/requests) for details on exactly what requests are made and how to opt-in to Google Analytics if you desire. @@ -183,11 +183,11 @@ See [requests made from the client](https://nextstrain.github.io/auspice/customi We improved the usability of the entropy (genomic diversity) panel, as well as fixing a few hidden bugs -- see [PR #771](https://github.com/nextstrain/auspice/pull/771). For instance, you can now see which codon a nucleotide codes for (and vice-versa). -![entropy](img/v2-entropy.gif) +![entropy](/blog/img/v2-entropy.gif) ## Auspice responds to server redirects for datasets -This allows custom servers ([nextstrain.org](https://nextstrain.org), for instance!) to smoothly inform Auspice that, e.g., a `getDataset` request to "/flu" (which doesn't actually exist) should be "/flu/seasonal/h3n2/ha/3y". +This allows custom servers ([nextstrain.org](/), for instance!) to smoothly inform Auspice that, e.g., a `getDataset` request to "/flu" (which doesn't actually exist) should be "/flu/seasonal/h3n2/ha/3y". See [PR #778](https://github.com/nextstrain/auspice/pull/778). diff --git a/static-site/content/blog/2019-10-31-using-narratives-to-explain-west-nile-virus-spread.md b/static-site/content/blog/2019-10-31-using-narratives-to-explain-west-nile-virus-spread.md index 914d89d43..d74ab81ba 100644 --- a/static-site/content/blog/2019-10-31-using-narratives-to-explain-west-nile-virus-spread.md +++ b/static-site/content/blog/2019-10-31-using-narratives-to-explain-west-nile-virus-spread.md @@ -40,7 +40,7 @@ At any time you can jump back to a “fully interactive” Nextstrain view & int So, the content of the paper we’ve just published is available as an interactive narrative at [nextstrain.org/narratives/twenty-years-of-WNV](/narratives/twenty-years-of-WNV). I encourage you to go and read it (by scrolling through each paragraph), interact with the underlying data (click “Explore the data yourself” in the top-right corner), and compare this to the paper we’ve just published. -![WNV Narrative demo](img/wnv_nextstrain_narrative.gif) +![WNV Narrative demo](/blog/img/wnv_nextstrain_narrative.gif) We’re only beginning to scratch the surface of different ways to present scientific data & findings — see [Brett Victor](http://worrydream.com)’s talks for a glimpse into the future. In a separate collaboration, we’ve been using narratives to provide situation-reports for the ongoing Ebola outbreak in the DRC every time new samples are sequenced, helping to bridge the gap between genomicists and epidemiologists. diff --git a/static-site/content/blog/2020-06-02-SARSCoV2-clade-naming.md b/static-site/content/blog/2020-06-02-SARSCoV2-clade-naming.md index 962bb24b1..3b0cd41b7 100644 --- a/static-site/content/blog/2020-06-02-SARSCoV2-clade-naming.md +++ b/static-site/content/blog/2020-06-02-SARSCoV2-clade-naming.md @@ -22,7 +22,7 @@ Within these major clades, we will monitor potential ‘emerging clades', which ### Definition of major clades We propose to name a new major clade when it reaches a frequency of 20% globally. When calculating these frequencies, care has to be taken to achieve approximately even sampling of sequences in time and space since sequencing effort varies strongly between countries. A clade name consists of the year it emerged and the next available letter in the alphabet. A new clade should be at least 2 mutations away from its parent major clade. -![nextstrain-global-ncov-clades](img/clades-2June.PNG) +![nextstrain-global-ncov-clades](/blog/img/clades-2June.PNG) **Fig 1.** Nextstrain ‘global’ run with the new Nextstrain major clades labelled. By these criteria, the first two clades are 19A and 19B which correspond to the split marked by mutations C8782T and T28144C. These clades were both prevalent in Asia during the first months of the outbreak. The next clade that was named is 20A corresponding to the clade that dominated large European outbreak in early 2020. It is distinguished from its parent 19A by the mutations C3037T, C14408T and A23403G. @@ -34,13 +34,13 @@ The clade definitions are coded in as a tabular file that defines a genotypic si Additionally, definitions of the clades, as well as a table of the current clades, with some detail on their characteristics and definition, is available on the ‘ncov’ github repository documentation (github.com/nextstrain/ncov) [here](https://github.com/nextstrain/ncov/blob/master/docs/clades.md). In parallel to the large-scale clade annotation, Nextstrain will support the Pangolin lineage nomenclature developed by Rambaut et al. 2020 (1), and continue to offer color-by for the ‘old’ Nextstrain clades so that exiting references to these clades are still identifiable. ### Using Nextstrain Clade Definitions -To make it easy for users to identify the Nextstrain clade of their own sequences, we provide a [simple python script](https://github.com/nextstrain/ncov/blob/master/assign_clades.py) that can be run on any Fasta file to assign appropriate clades. This script is part of the ‘ncov’ github repository, but does not require running any other part of the pipeline. However ‘augur’ must be installed to run the script. This can be done [a number of different ways](https://nextstrain.org/docs/getting-started/local-installation#install-augur-with-python), but is often most easily done [using ‘pip’](https://nextstrain-augur.readthedocs.io/en/stable/installation/installation.html#using-pip-from-pypi). +To make it easy for users to identify the Nextstrain clade of their own sequences, we provide a [simple python script](https://github.com/nextstrain/ncov/blob/master/assign_clades.py) that can be run on any Fasta file to assign appropriate clades. This script is part of the ‘ncov’ github repository, but does not require running any other part of the pipeline. However ‘augur’ must be installed to run the script. This can be done [a number of different ways](/docs/getting-started/local-installation#install-augur-with-python), but is often most easily done [using ‘pip’](https://nextstrain-augur.readthedocs.io/en/stable/installation/installation.html#using-pip-from-pypi). **Links:** * An up-to-date description of our approach to clade names and the current clades can be found on the Nextstrain ‘ncov’ github [here](https://github.com/nextstrain/ncov/blob/master/docs/clades.md). -* Clades show on the current Nextstrain 'Global' SARS-CoV-2 tree can be viewed [here](https://nextstrain.org/ncov/global?branchLabel=clade&c=clade_membership). -* Pangolin clades can be displayed on the Nextstrain runs, as demonstrated [here](https://nextstrain.org/ncov/global?branchLabel=none&c=pangolin_lineage). -* The ‘old’ Nextstrain clades can still be viewed [here](https://nextstrain.org/ncov/global?branchLabel=none&c=legacy_clade_membership) as a color-by for those papers/references that have used them +* Clades show on the current Nextstrain 'Global' SARS-CoV-2 tree can be viewed [here](/ncov/global?branchLabel=clade&c=clade_membership). +* Pangolin clades can be displayed on the Nextstrain runs, as demonstrated [here](/ncov/global?branchLabel=none&c=pangolin_lineage). +* The ‘old’ Nextstrain clades can still be viewed [here](/ncov/global?branchLabel=none&c=legacy_clade_membership) as a color-by for those papers/references that have used them References: diff --git a/static-site/content/blog/2021-01-06-updated-SARS-CoV-2-clade-naming.md b/static-site/content/blog/2021-01-06-updated-SARS-CoV-2-clade-naming.md index 8993618de..d423ff7fa 100644 --- a/static-site/content/blog/2021-01-06-updated-SARS-CoV-2-clade-naming.md +++ b/static-site/content/blog/2021-01-06-updated-SARS-CoV-2-clade-naming.md @@ -9,7 +9,7 @@ The emerging 501Y.V1 and 501Y.V2 variants have pushed the Nextstrain team to rev In June we put forth an [initial Nextstrain clade naming strategy](/blog/2020-06-02-SARSCoV2-clade-naming/). This basic strategy of flat "year-letter" names was borne out of work with seasonal influenza, where the nested names of 3c2.A1b (etc...) can become unwieldy. In the “year-letter” scheme, years are there to make it easy to know what's being discussed in ~5 years when, for example, clade `20A` is referenced. Our June strategy called for naming of a clade when it reached >20% global frequency for more than 2 months. -However, as the pandemic progressed, lack of international travel made it so that no clades beyond the initial clades `20A`, `20B` and `20C` made it past 20% global frequency. Instead, we've seen "regional" clades that hit appreciable frequency in different continent-level regions of the world. One example is `20A.EU1`, which has risen to [high frequency in Europe in particular](https://nextstrain.org/ncov/europe?c=clade_membership&f_region=Europe&transmissions=hide). When clusters like `20A.EU1` and `20A.EU2` were originally [described in October](https://www.medrxiv.org/content/10.1101/2020.10.25.20219063v2), it seemed like labeling based on regional vs global circulation was of benefit. However, the emergence of fast-spreading "variants" has made it clear that a region-based naming system will have drawbacks when spread is rapidly more global. +However, as the pandemic progressed, lack of international travel made it so that no clades beyond the initial clades `20A`, `20B` and `20C` made it past 20% global frequency. Instead, we've seen "regional" clades that hit appreciable frequency in different continent-level regions of the world. One example is `20A.EU1`, which has risen to [high frequency in Europe in particular](/ncov/europe?c=clade_membership&f_region=Europe&transmissions=hide). When clusters like `20A.EU1` and `20A.EU2` were originally [described in October](https://www.medrxiv.org/content/10.1101/2020.10.25.20219063v2), it seemed like labeling based on regional vs global circulation was of benefit. However, the emergence of fast-spreading "variants" has made it clear that a region-based naming system will have drawbacks when spread is rapidly more global. Additionally, we recognize that complex, unintuitive names lead to geographic-based terms like "UK variant", which can be harmful to the country involved. Therefore, having relatively simple, intuitive official names without geography is important. Finally, there is the issue where if `20A.EU1` did expand to >20% global frequency, it would be confusing to relabel it from `20A.EU1` to, for example, `20E`. @@ -19,17 +19,17 @@ Consequently, we propose an updated strategy, where major (year-letter) clades a 3. A VOC ('variant of concern') is recognized (applies currently to 501Y.V1 and 501Y.V2) This results in the [updated clade definitions](https://github.com/nextstrain/ncov/blob/master/defaults/clades.tsv), and the resulting Nextstrain outputs can be seen at: -- [Global](https://nextstrain.org/ncov/global?c=clade_membership) -- [Africa](https://nextstrain.org/ncov/africa?c=clade_membership&f_region=Africa&transmissions=hide) -- [Asia](https://nextstrain.org/ncov/asia?c=clade_membership&f_region=Asia&transmissions=hide) -- [Europe](https://nextstrain.org/ncov/europe?c=clade_membership&f_region=Europe&transmissions=hide) -- [North America](https://nextstrain.org/ncov/north-america?c=clade_membership&f_region=North%20America&transmissions=hide) -- [Oceania](https://nextstrain.org/ncov/oceania?c=clade_membership&f_region=Oceania&transmissions=hide) -- [South America](https://nextstrain.org/ncov/south-america?c=clade_membership&f_region=South%20America&transmissions=hide) +- [Global](/ncov/global?c=clade_membership) +- [Africa](/ncov/africa?c=clade_membership&f_region=Africa&transmissions=hide) +- [Asia](/ncov/asia?c=clade_membership&f_region=Asia&transmissions=hide) +- [Europe](/ncov/europe?c=clade_membership&f_region=Europe&transmissions=hide) +- [North America](/ncov/north-america?c=clade_membership&f_region=North%20America&transmissions=hide) +- [Oceania](/ncov/oceania?c=clade_membership&f_region=Oceania&transmissions=hide) +- [South America](/ncov/south-america?c=clade_membership&f_region=South%20America&transmissions=hide) There are 9 major clades identified for 2020 with 6 new clades being added in addition to the original `20A`, `20B` and `20C`. These are `20A` through `20I`. Ordering is based on estimated TMRCA following our original proposal: "we propose to name major clades by the year they are estimated to have emerged and a letter, e.g. `19A`, `19B`, `20A`." Clade 20E (EU1) is the elevated clade `20A.EU1` where the "EU1" parenthetical is retained to help connect these labels. -![nextstrain-global-ncov-clades](img/ncov_tree_2020_01_06.png) +![nextstrain-global-ncov-clades](/blog/img/ncov_tree_2020_01_06.png) **Fig 1.** Nextstrain 'global' run with the new Nextstrain major clades labelled. Importantly, we propose to dual label major clades if they correspond to an emerging “variant of concern” (VOC), so that we have `20H/501Y.V2` and `20I/501Y.V1`. We believe it's useful and informative to have the genetic short-hand for these, as it is self-documenting. In a hypothetical example, if we had a future VOC bearing a hallmark spike 484K mutation, then this variant would be labeled 484K.V1. In this case, these variants are labeled by the relevant spike mutation along with V1, V2, etc. to disambiguate. These disambiguating numbers are assigned in order of identification or announcement. @@ -41,15 +41,15 @@ Moving forward, we aim to avoid relabeling. Making clusters of interest major cl Going forward, we'll commit to keeping these major clades fresher. In this system we estimate 7 clades currently at >5% global frequency and regions with between 2 and 5 clades currently circulating at >5% regional frequency. We feel this is a good resolution to capture important dynamics without being too overwhelming. Additionally, we hope this modified strategy, outlined above, will prevent us from having to relabel in the future. At this moment, major clades from 2020 onwards are: -- [`20A`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20A): basal pandemic lineage bearing S 614G that's globally distributed -- [`20B`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20B): derived from 20A bearing N 203K, N204R and ORF14 50N, also globally distributed -- [`20C`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20C): derived from 20A bearing ORF3a 57H and ORF1a 265I, also globally distributed -- [`20D`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20D): derived from 20B bearing ORF1a 1246I and ORF1a 3278S, concentrated in South America, southern Europe and South Africa -- [`20E`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20E): derived from 20A bearing N 220V, ORF10 30L, ORF14 67F and S 222V, concentrated in Europe -- [`20F`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20F): derived from 20B bearing ORF1a 300F and S 477N, concentrated in Australia -- [`20G`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20G): derived from 20C bearing ORF1b 1653D, ORF3a 172V, N 67S and N 199L, concentrated in the United States -- [`20H/501Y.V2`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20H/501Y.V2): derived from 20C bearing S 80A, S 215G, S 484K, S 501Y, S 701V, concentrated in South Africa -- [`20I/501Y.V1`](https://nextstrain.org/ncov/global?c=clade_membership&f_clade_membership=20I/501Y.V1): derived from 20B bearing S 501Y, S 570D, S 681H, ORF8 27*, concentrated in the United Kingdom - -![ncov-clades-schematic](img/ncov_clades_schematic_2021_01_6.png) +- [`20A`](/ncov/global?c=clade_membership&f_clade_membership=20A): basal pandemic lineage bearing S 614G that's globally distributed +- [`20B`](/ncov/global?c=clade_membership&f_clade_membership=20B): derived from 20A bearing N 203K, N204R and ORF14 50N, also globally distributed +- [`20C`](/ncov/global?c=clade_membership&f_clade_membership=20C): derived from 20A bearing ORF3a 57H and ORF1a 265I, also globally distributed +- [`20D`](/ncov/global?c=clade_membership&f_clade_membership=20D): derived from 20B bearing ORF1a 1246I and ORF1a 3278S, concentrated in South America, southern Europe and South Africa +- [`20E`](/ncov/global?c=clade_membership&f_clade_membership=20E): derived from 20A bearing N 220V, ORF10 30L, ORF14 67F and S 222V, concentrated in Europe +- [`20F`](/ncov/global?c=clade_membership&f_clade_membership=20F): derived from 20B bearing ORF1a 300F and S 477N, concentrated in Australia +- [`20G`](/ncov/global?c=clade_membership&f_clade_membership=20G): derived from 20C bearing ORF1b 1653D, ORF3a 172V, N 67S and N 199L, concentrated in the United States +- [`20H/501Y.V2`](/ncov/global?c=clade_membership&f_clade_membership=20H/501Y.V2): derived from 20C bearing S 80A, S 215G, S 484K, S 501Y, S 701V, concentrated in South Africa +- [`20I/501Y.V1`](/ncov/global?c=clade_membership&f_clade_membership=20I/501Y.V1): derived from 20B bearing S 501Y, S 570D, S 681H, ORF8 27*, concentrated in the United Kingdom + +![ncov-clades-schematic](/blog/img/ncov_clades_schematic_2021_01_6.png) **Fig 2.** Schematic showing hierarchical relationships among clades. An interactive version of this diagram is available [here](https://bl.ocks.org/trvrb/66b2c193117220200eb8e09190a43c85). diff --git a/static-site/content/blog/2021-07-08-ncov-open-announcement.md b/static-site/content/blog/2021-07-08-ncov-open-announcement.md index 2e5650c1a..0e5b544fc 100644 --- a/static-site/content/blog/2021-07-08-ncov-open-announcement.md +++ b/static-site/content/blog/2021-07-08-ncov-open-announcement.md @@ -11,10 +11,10 @@ With Nextstrain, we’ve done our best to respect these Terms of Use and we enco Our goal with Nextstrain is to harness the scientific and public health potential of pathogen genome data and we’ve built our software to be agnostic to the source of data. Genomic surveillance efforts particularly in the US, UK, Germany, Switzerland, Australia, Bangladesh, Chile, Egypt, Ghana, India, Kenya, and Peru have been sharing SARS-CoV-2 genomic sequence data to [INSDC](http://www.insdc.org/) databases, which mirror data between [GenBank](https://www.ncbi.nlm.nih.gov/genbank/), [ENA](https://www.ebi.ac.uk/ena/browser/home) and [DDBJ](https://www.ddbj.nig.ac.jp/). Data shared to GenBank and other INSDC databases is shared under Open Data principles. "[Open Data is data that can be freely used, re-used and redistributed by anyone - subject only, at most, to the requirement to attribute and sharealike](https://opendatahandbook.org/guide/en/what-is-open-data/)". This allows us at Nextstrain to share sequence data and metadata from GenBank, as well as intermediate files, in an open fashion and help to facilitate data use by academic and public health bioinformaticians. -![gisaid_vs_open_map](img/gisaid_vs_open_map.png) +![gisaid_vs_open_map](/blog/img/gisaid_vs_open_map.png) **Fig 1.** Geographic distribution of samples included in "global" build with GISAID data and Open Data -Moving forward, we will be simultaneously curating and keeping continually updated analyses for GISAID data at [nextstrain.org/ncov/gisaid](https://nextstrain.org/ncov/gisaid) as well as Open Data at [nextstrain.org/ncov/open](https://nextstrain.org/ncov/open), where we are mirroring the pattern of one "global" build alongside six "regional" builds (Fig. 1). Open data is fetched from NCBI GenBank in a completely separate process from data fetched from GISAID. To maximize the utility and visibility of shared open datasets, we also provide preprocessed files that can serve as a starting point for additional analyses, including flat files of aligned sequences and curated metadata. Please see our [documentation](https://docs.nextstrain.org/projects/ncov/en/latest/reference/remote_inputs.html) on how to use these preprocessed files. Briefly, this allows Nextstrain build config files to point at URLs like [data.nextstrain.org/files/ncov/open/metadata.tsv.gz](https://data.nextstrain.org/files/ncov/open/metadata.tsv.gz), providing an automated starting point which lets users avoid having to click through a web interface to provision data. This should allow for more automated rebuilds of public health dashboards. Following Terms of Use, we keep GISAID data secure and only provide access to these intermediate data files for GenBank data. +Moving forward, we will be simultaneously curating and keeping continually updated analyses for GISAID data at [nextstrain.org/ncov/gisaid](/ncov/gisaid) as well as Open Data at [nextstrain.org/ncov/open](/ncov/open), where we are mirroring the pattern of one "global" build alongside six "regional" builds (Fig. 1). Open data is fetched from NCBI GenBank in a completely separate process from data fetched from GISAID. To maximize the utility and visibility of shared open datasets, we also provide preprocessed files that can serve as a starting point for additional analyses, including flat files of aligned sequences and curated metadata. Please see our [documentation](https://docs.nextstrain.org/projects/ncov/en/latest/reference/remote_inputs.html) on how to use these preprocessed files. Briefly, this allows Nextstrain build config files to point at URLs like [data.nextstrain.org/files/ncov/open/metadata.tsv.gz](https://data.nextstrain.org/files/ncov/open/metadata.tsv.gz), providing an automated starting point which lets users avoid having to click through a web interface to provision data. This should allow for more automated rebuilds of public health dashboards. Following Terms of Use, we keep GISAID data secure and only provide access to these intermediate data files for GenBank data. Please note that although data generators have generously shared data in an open fashion, _that does not mean there should be a free license to publish on this data_. At Nextstrain we strongly believe in appropriate credit and recognise that "scooping" can make future data generation harder and less appealing to share, and can be particularly problematic for scientists from low and middle income countries. Data generators should be cited where possible and collaborations should be sought in some circumstances. To facilitate such attribution, the open metadata includes links to the original records as well as author information where available. Please try to avoid scooping someone else's work, be mindful of what plans others might have, and reach out if uncertain. diff --git a/static-site/content/blog/2022-04-29-SARS-CoV-2-clade-naming-2022.md b/static-site/content/blog/2022-04-29-SARS-CoV-2-clade-naming-2022.md index 62068a3bc..c0df912ef 100644 --- a/static-site/content/blog/2022-04-29-SARS-CoV-2-clade-naming-2022.md +++ b/static-site/content/blog/2022-04-29-SARS-CoV-2-clade-naming-2022.md @@ -28,9 +28,9 @@ As a result of this, we are designating 3 new Nextstrain clades: As all of these variants are part of the Omicron family and the Omicron group is designated a VOC according to WHO, they will retain the parenthetical "(Omicron)" designation. Note that the topology at the base of clade `21L` that contains Pango lineages BA.2, BA.4 and BA.5 is not completely certain. BA.2, BA.4 and BA.5 are designated as sister lineages. Here, we’ve placed clade `21L` along with `22A` and `22B` within `21L` to be robust to noise in phylogenetic reconstruction. -You can view updated clade definitions and detailed rationale behind elevating the new clades in this [GitHub PR](https://github.com/nextstrain/ncov/pull/933), and the resulting Nextstrain outputs can be seen at [nextstrain.org/ncov/](https://nextstrain.org/ncov/). +You can view updated clade definitions and detailed rationale behind elevating the new clades in this [GitHub PR](https://github.com/nextstrain/ncov/pull/933), and the resulting Nextstrain outputs can be seen at [nextstrain.org/ncov/](/ncov/). An updated version of our clade schema diagram is now: -![ncov-clades-schematic-apr-2022](img/ncov_clades_schematic_2022_04_29.png) +![ncov-clades-schematic-apr-2022](/blog/img/ncov_clades_schematic_2022_04_29.png) **Fig 1.** Schematic showing hierarchical relationships among clades. The code behind this diagram is available [here](https://github.com/nextstrain/ncov-clades-schema). diff --git a/static-site/content/blog/2024-03-27-annual-update-march-2024.md b/static-site/content/blog/2024-03-27-annual-update-march-2024.md index 0420d26db..dd7e3fe3d 100644 --- a/static-site/content/blog/2024-03-27-annual-update-march-2024.md +++ b/static-site/content/blog/2024-03-27-annual-update-march-2024.md @@ -17,13 +17,13 @@ as we believe that proper tooling for real-time surveillance (1) enables rapid r ## Current composition of the core team -As open source software, we have contributions to the codebase from unaffiliated individuals, but Nextstrain is largely a product of a [core developer team](https://nextstrain.org/team/) consisting of lab members of the Bedford Lab at the Fred Hutchinson Cancer Center in Seattle and the Neher Lab at the University of Basel. Seattle members include Kim Andrews (bioinformatician), Jennifer Chang (bioinformatician), James Hadfield (consultant), John Huddleston (staff scientist), Jover Lee (software developer), Victor Lin (software developer) and Tom Sibley (software developer). Basel members include Ivan Aksamentov (software developer) and Cornelius Roemer (staff scientist). In addition, Emma Hodcroft remains a core member as she starts her own research group at the Swiss Tropical and Public Health Institute in Basel. +As open source software, we have contributions to the codebase from unaffiliated individuals, but Nextstrain is largely a product of a [core developer team](/team/) consisting of lab members of the Bedford Lab at the Fred Hutchinson Cancer Center in Seattle and the Neher Lab at the University of Basel. Seattle members include Kim Andrews (bioinformatician), Jennifer Chang (bioinformatician), James Hadfield (consultant), John Huddleston (staff scientist), Jover Lee (software developer), Victor Lin (software developer) and Tom Sibley (software developer). Basel members include Ivan Aksamentov (software developer) and Cornelius Roemer (staff scientist). In addition, Emma Hodcroft remains a core member as she starts her own research group at the Swiss Tropical and Public Health Institute in Basel. ## Recent activities (mid-2022 to present) Work on SARS-CoV-2 was all-consuming throughout 2020 and 2021. However, after the Omicron wave in early 2022, we were able to start to move away from emergency response and subsequent to mid-2022 we have returned to other pathogens and to broader development directions. -In terms of **SARS-CoV-2**, we've continued to assist with nomenclature and clade / lineage classification from sequence data. We've designated [40 Nextstrain clades](https://github.com/nextstrain/ncov-clades-schema) that meet quantitative thresholds for regional or global frequencies or frequency growth and have assisted in the designation of Pango lineages. We maintain [Nextclade](https://clades.nextstrain.org/) as a method to quickly classify user genomic data into corresponding clades and lineages. Such classification has enabled Multinomial Logistic Regression (MLR) models for [short-term forecasts of clade and lineage frequencies](https://nextstrain.org/sars-cov-2/forecasts/). In addition, we've continued to iterate on the "ncov" workflow for phylogenetic analysis of SARS-CoV-2 including additions like [focal builds for clade 21L (lineage BA.2) descended viruses](https://nextstrain.org/ncov/gisaid/21L/global/all-time) and [calculations of immune escape](https://nextstrain.org/ncov/gisaid/21L/global/all-time?c=immune_escape) based on deep mutational scanning data. The "ncov" workflow has automated ingest from GISAID and automated phylogenetic rebuilds. +In terms of **SARS-CoV-2**, we've continued to assist with nomenclature and clade / lineage classification from sequence data. We've designated [40 Nextstrain clades](https://github.com/nextstrain/ncov-clades-schema) that meet quantitative thresholds for regional or global frequencies or frequency growth and have assisted in the designation of Pango lineages. We maintain [Nextclade](https://clades.nextstrain.org/) as a method to quickly classify user genomic data into corresponding clades and lineages. Such classification has enabled Multinomial Logistic Regression (MLR) models for [short-term forecasts of clade and lineage frequencies](/sars-cov-2/forecasts/). In addition, we've continued to iterate on the "ncov" workflow for phylogenetic analysis of SARS-CoV-2 including additions like [focal builds for clade 21L (lineage BA.2) descended viruses](/ncov/gisaid/21L/global/all-time) and [calculations of immune escape](/ncov/gisaid/21L/global/all-time?c=immune_escape) based on deep mutational scanning data. The "ncov" workflow has automated ingest from GISAID and automated phylogenetic rebuilds. Regarding **other pathogens**, much of our focus has been on seasonal influenza, mpox and RSV. For mpox and RSV we have automated data ingest from GenBank (seasonal flu requires downloads from GISAID via browser) and for all three we have automated phylogenetic builds. We've provided updated nomenclature for all three with [seasonal flu clades and subclades](https://github.com/influenza-clade-nomenclature), [mpox lineages](https://github.com/mpxv-lineages), and [RSV lineages](https://github.com/rsv-lineages). In each case, we've set up a specific GitHub organization to host nomenclature along with associated clade and lineage definition files. We continue to work with the US CDC and WHO GISRS to analyze seasonal influenza genetic and antigenic diversity, forecast clade frequencies and to provide technical reports for biannual vaccine composition meetings. @@ -48,7 +48,7 @@ It remains challenging for us to winnow down strategic priorities. Here, we've i 1. **Building out automated workflows to ~20 pathogens**. Expanding the number of "core" pathogens that have automated ingest and phylogenetics would address the original remit of Nextstrain. Each core pathogen provides a real-time view on nextstrain.org, but also curated data and a starting point for user analyses. 2. **Improvements to Nextclade**. Planned directions include calling mutations relative to different references, identification of clade founders and redoing the alignment viewer. -3. **Frequencies-focused visualization app**. We've made initial strides to visualizing frequency data (separately for [SARS-CoV-2](https://nextstrain.org/sars-cov-2/forecasts/) and [seasonal influenza](https://flu-frequencies.vercel.app/)). We'd like to consolidate these efforts and provide a platform for evolutionary forecasts for SARS-CoV-2 and seasonal influenza. +3. **Frequencies-focused visualization app**. We've made initial strides to visualizing frequency data (separately for [SARS-CoV-2](/sars-cov-2/forecasts/) and [seasonal influenza](https://flu-frequencies.vercel.app/)). We'd like to consolidate these efforts and provide a platform for evolutionary forecasts for SARS-CoV-2 and seasonal influenza. 4. **Scaling and other Auspice improvements**. It remains a consistent frustration to have phylogenetic analyses limited to ~5000 samples. This limitation is largely due to JavaScript performance of manipulating thousands of SVG objects. Even without resorting to WebGL there should be strategies to improve scaling of Auspice. And additionally there remain many basic visualization improvements (notably tree zoom and manipulation) to be implemented. 5. **Improvements to nextstrain.org**. We'd like to continue to build out platform aspects to better surface datasets, and also to provide a GUI interface for modifying Groups data. 6. **Improvements to documentation**. We'd like to provide enough documentation for outside users to use and modify existing pathogen workflows and also to create their own bespoke workflows. This includes common issues of subsampling and data ingest. diff --git a/static-site/content/blog/2024-06-12-new-resources-for-measles.md b/static-site/content/blog/2024-06-12-new-resources-for-measles.md index 4af095b3e..5bb561619 100644 --- a/static-site/content/blog/2024-06-12-new-resources-for-measles.md +++ b/static-site/content/blog/2024-06-12-new-resources-for-measles.md @@ -5,7 +5,7 @@ title: "New Resources for Measles Virus" sidebarTitle: "New Resources for Measles" --- -We now provide regularly updated phylogenetic monitoring of measles virus at [nextstrain.org/measles](https://nextstrain.org/measles). This site displays phylogenies generated using genomic data from NCBI GenBank, and is updated daily when new sequences are uploaded to NCBI. You can choose to display one of two different phylogenies based on the following data types: +We now provide regularly updated phylogenetic monitoring of measles virus at [nextstrain.org/measles](/measles). This site displays phylogenies generated using genomic data from NCBI GenBank, and is updated daily when new sequences are uploaded to NCBI. You can choose to display one of two different phylogenies based on the following data types: 1. Full genome sequences 1. 450bp region of the N gene ("N450") @@ -19,11 +19,11 @@ We also provide links, updated daily, where all NCBI GenBank measles [sequences] With [measles cases currently increasing globally](https://www.who.int/news/item/16-11-2023-global-measles-threat-continues-to-grow-as-another-year-passes-with-millions-of-children-unvaccinated), we expect these tools to be useful resources for tracking the spread of this viral pathogen. This work is made possible by the open sharing of genetic data by research groups from all over the world. We gratefully acknowledge their contributions. -[![tree-genome](img/measles_tree_genome_2024-06-10.png)](https://nextstrain.org/measles/genome) +[![tree-genome](/blog/img/measles_tree_genome_2024-06-10.png)](/measles/genome) **Fig 1.** Phylogeny of full genome sequences for measles. -[![tree-N450](img/measles_tree_N450_2024-06-10.png)](https://nextstrain.org/measles/N450) +[![tree-N450](/blog/img/measles_tree_N450_2024-06-10.png)](/measles/N450) **Fig 2.** Phylogeny of N450 sequences for measles. -[![measles-nextclade](img/measles_nextclade.png)](https://clades.nextstrain.org/?dataset-name=nextstrain/measles/N450/WHO-2012) +[![measles-nextclade](/blog/img/measles_nextclade.png)](https://clades.nextstrain.org/?dataset-name=nextstrain/measles/N450/WHO-2012) **Fig 3.** Example of output for the measles Nextclade dataset. diff --git a/static-site/content/blog/2024-06-18-h5n1-cattle-outbreak-analysis-and-resources.md b/static-site/content/blog/2024-06-18-h5n1-cattle-outbreak-analysis-and-resources.md index e024c1e2d..0d00301a4 100644 --- a/static-site/content/blog/2024-06-18-h5n1-cattle-outbreak-analysis-and-resources.md +++ b/static-site/content/blog/2024-06-18-h5n1-cattle-outbreak-analysis-and-resources.md @@ -8,54 +8,54 @@ sidebarTitle: "H5N1 Cattle Outbreak Analysis and Resources" # Phylogenetic analysis The 2.3.4.4b clade of influenza subtype H5N1 has been remarkably successful in its global spread with arrival into Europe in mid-2020, into North America in late 2021 and into South America in mid-2022 (**Fig. 1**). -Although sustained transmission and spread has been largely within birds, these viruses have infected a number of species of non-human mammals, including [foxes, skunks and domestic cats](https://nextstrain.org/groups/moncla-lab/h5nx/north-america/ha?c=species_group&f_species_group=Mammal-%20Terrestrial&r=division&transmissions=hide) which appear to be dead-end hosts to infection, but also [seals in North America](https://nextstrain.org/groups/moncla-lab/h5nx/north-america/ha?c=species_group&f_species_group=Mammal-%20Marine&r=division&transmissions=hide) and [sea lions in South America](https://www.nature.com/articles/s41467-023-41182-0), where sustained transmission outside of birds is highly likely. +Although sustained transmission and spread has been largely within birds, these viruses have infected a number of species of non-human mammals, including [foxes, skunks and domestic cats](/groups/moncla-lab/h5nx/north-america/ha?c=species_group&f_species_group=Mammal-%20Terrestrial&r=division&transmissions=hide) which appear to be dead-end hosts to infection, but also [seals in North America](/groups/moncla-lab/h5nx/north-america/ha?c=species_group&f_species_group=Mammal-%20Marine&r=division&transmissions=hide) and [sea lions in South America](https://www.nature.com/articles/s41467-023-41182-0), where sustained transmission outside of birds is highly likely. -[![fig1](img/h5n1_cattle_outbreak_fig1_ha_all_time_region.png)](https://nextstrain.org/avian-flu/h5n1/ha/all-time@2024-06-18) +[![fig1](/blog/img/h5n1_cattle_outbreak_fig1_ha_all_time_region.png)](/avian-flu/h5n1/ha/all-time@2024-06-18) **Figure 1. Broader context of H5N1 evolution and spread.** -Available as [nextstrain.org/avian-flu/h5n1/ha/all-time](https://nextstrain.org/avian-flu/h5n1/ha/all-time@2024-06-18). +Available as [nextstrain.org/avian-flu/h5n1/ha/all-time](/avian-flu/h5n1/ha/all-time@2024-06-18). More recently, this clade of H5N1 was discovered circulating in dairy cows in the US with cases [first reported in Texas in March 25, 2024](https://www.aphis.usda.gov/news/agency-announcements/federal-state-veterinary-public-health-agencies-share-update-hpai). Consensus genomes from infected dairy cattle have been shared to GenBank primarily by the [National Veterinary Services Laboratories (NVSL)](https://www.aphis.usda.gov/labs/about-nvsl) of the [Animal and Plant Health Inspection Service (APHIS)](https://www.aphis.usda.gov/) of the U.S. Department of Agriculture (USDA). We find, in accordance to results by [Nguyen et al](https://www.biorxiv.org/content/10.1101/2024.05.01.591751v1) and by [Worobey et al](https://virological.org/t/preliminary-report-on-genomic-epidemiology-of-the-2024-h5n1-influenza-a-virus-outbreak-in-u-s-cattle-part-1-of-2/970), that cattle-associated viruses form a distinct monophyletic clade across all 8 segments (**Fig. 2**). -Comparing phylogenetic structure across segments shows reassortment in birds prior to cattle spillover, but that the [cattle clade appears to be non-reassorting](https://nextstrain.org/avian-flu/h5n1/ha/2y@2024-06-18:avian-flu/h5n1/pb2/2y@2024-06-18?c=host&m=div). +Comparing phylogenetic structure across segments shows reassortment in birds prior to cattle spillover, but that the [cattle clade appears to be non-reassorting](/avian-flu/h5n1/ha/2y@2024-06-18:avian-flu/h5n1/pb2/2y@2024-06-18?c=host&m=div). -[![fig2](img/h5n1_cattle_outbreak_fig2_across_segments_2y_host.png)](https://nextstrain.org/avian-flu/h5n1/ha/2y@2024-06-18?c=host) +[![fig2](/blog/img/h5n1_cattle_outbreak_fig2_across_segments_2y_host.png)](/avian-flu/h5n1/ha/2y@2024-06-18?c=host) **Figure 2. Cattle infections form a distinct clade across segments indicative of single spillover and cow-to-cow spread.** -Segment-level trees available for [HA](https://nextstrain.org/avian-flu/h5n1/ha/2y@2024-06-18?c=host), [PB2](https://nextstrain.org/avian-flu/h5n1/pb2/2y@2024-06-18?c=host), [PA](https://nextstrain.org/avian-flu/h5n1/pa/2y@2024-06-18?c=host), etc... +Segment-level trees available for [HA](/avian-flu/h5n1/ha/2y@2024-06-18?c=host), [PB2](/avian-flu/h5n1/pb2/2y@2024-06-18?c=host), [PA](/avian-flu/h5n1/pa/2y@2024-06-18?c=host), etc... -Consequently, we have constructed a full genome phylogenetic analysis at [nextstrain.org/avian-flu/h5n1-cattle-outbreak/](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome) showing evolution and spread in cattle (**Fig. 3**). +Consequently, we have constructed a full genome phylogenetic analysis at [nextstrain.org/avian-flu/h5n1-cattle-outbreak/](/avian-flu/h5n1-cattle-outbreak/genome) showing evolution and spread in cattle (**Fig. 3**). This treats concatenated segments as a standard full genome analysis by using a synthetic reference sequence and gene coordinates on this reference sequence. The resulting tree has significantly more phylogenetic resolution than single genome trees. -This difference can be seen in the reduced number of polytomies (identical sequences) in the [full genome analysis](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?m=div) compared to [single segment analyses](https://nextstrain.org/avian-flu/h5n1/ha/2y?c=host&f_host=Cattle&m=div). -With increased phylogenetic resolution, we observe evidence for [clock signal within the cattle outbreak clade](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?branches=hide&f_data_source=genbank&l=scatter®ression=show&scatterY=div). +This difference can be seen in the reduced number of polytomies (identical sequences) in the [full genome analysis](/avian-flu/h5n1-cattle-outbreak/genome?m=div) compared to [single segment analyses](/avian-flu/h5n1/ha/2y?c=host&f_host=Cattle&m=div). +With increased phylogenetic resolution, we observe evidence for [clock signal within the cattle outbreak clade](/avian-flu/h5n1-cattle-outbreak/genome?branches=hide&f_data_source=genbank&l=scatter®ression=show&scatterY=div). Stronger clock signal and greater phylogenetic resolution allow for more fine-grained estimates of spatiotemporal patterns than available from single segments. -[![fig3](img/h5n1_cattle_outbreak_fig3_full_genome_div.png)](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?m=div) +[![fig3](/blog/img/h5n1_cattle_outbreak_fig3_full_genome_div.png)](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?m=div) **Figure 3. Full genome analysis of concatenated segments provides substantially finer resolution.** -Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?m=div](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?m=div). +Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?m=div](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?m=div). We estimate the common ancestor of sampled cattle sequences to be early February 2024 with a 95% confidence interval of late Nov 2023 to late Feb 2024. Additionally, we estimate this common ancestor to exist in Texas congruent with initial cases (**Fig. 4**). However, Texas is more heavily sampled than other states with 52% (136 of 260) of viruses sampled from Texas. This sampling bias may result in over-confidence in the inferred Texas location for the common ancestor. -[![fig4](img/h5n1_cattle_outbreak_fig4_time_admin_division.png)](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=division) +[![fig4](/blog/img/h5n1_cattle_outbreak_fig4_time_admin_division.png)](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=division) **Figure 4. Epizootic origin appears to be Texas congruent with initial cases.** -Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?c=division](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=division). +Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome?c=division](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=division). So far [three human cases](https://www.cdc.gov/flu/avianflu/avian-flu-summary.htm) have been linked to this outbreak and we include genome sequences for two of these. The first case has no proximal sequenced viruses which perhaps indicates a gap in sampling around the cattle cases which led to this infection, while the [second case](https://www.cdc.gov/media/releases/2024/s0522-human-case-h5.html), a worker on a dairy farm in Michigan, groups with cattle from Texas suggesting unsampled cattle-cattle transmission from Texas to Michigan which subsequently infected the worker (**Fig. 5**). Recently there was a [fatal human case of H5N2 avian flu in Mexico](https://www.who.int/emergencies/disease-outbreak-news/item/2024-DON520) however this is a different influenza subtype from the H5N1 viruses that are related to the cattle outbreak. -[![fig5](img/h5n1_cattle_outbreak_fig5_time_host.png)](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18) +[![fig5](/blog/img/h5n1_cattle_outbreak_fig5_time_host.png)](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18) **Figure 5. One human case nests outside of sequenced cattle diversity, while the other nests within.** -Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18). +Available as [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18). Specific mutations to the viral RNA polymerase are associated with the cattle outbreak, most obviously M631L in PB2 (**Fig. 6**), which may facilitate interaction with host protein ANP32 ([Worobey et al](https://virological.org/t/preliminary-report-on-genomic-epidemiology-of-the-2024-h5n1-influenza-a-virus-outbreak-in-u-s-cattle-part-1-of-2/970)). One-off human infections by H5N1 commonly evolved a similar mutation in PB2 E627K. -Notably, the human case that outgroups to the cattle clade [evolved E627K](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=gt-PB2_627&f_host=Human&gmax=2307&gmin=28), while the human case that nests within the cattle clade did not, possibly because M631L is providing a similar benefit to the virus. +Notably, the human case that outgroups to the cattle clade [evolved E627K](/avian-flu/h5n1-cattle-outbreak/genome@2024-06-18?c=gt-PB2_627&f_host=Human&gmax=2307&gmin=28), while the human case that nests within the cattle clade did not, possibly because M631L is providing a similar benefit to the virus. -[![fig6](img/h5n1_cattle_outbreak_fig6_pb2_2y_gt631.png)](https://nextstrain.org/avian-flu/h5n1/pb2/2y@2024-06-18?c=gt-PB2_631&l=scatter&m=div&r=division&scatterX=host&scatterY=gt) +[![fig6](/blog/img/h5n1_cattle_outbreak_fig6_pb2_2y_gt631.png)](/avian-flu/h5n1/pb2/2y@2024-06-18?c=gt-PB2_631&l=scatter&m=div&r=division&scatterX=host&scatterY=gt) **Figure 6. Association of PB2 mutation M631L with cattle infections.** Sequenced cattle-infecting viruses all possess PB2 mutation M631L. @@ -82,7 +82,7 @@ During our ingest process, we merge these assembled consensus genomes with conse GenBank was updated with 280 genomes on May 3 by the USDA corresponding to data described in the [Nyugen et al preprint](https://www.biorxiv.org/content/10.1101/2024.05.01.591751v1), but following this there have been only 27 genomes shared to GenBank by the USDA since May 3 (**Fig. 7**). However, sharing to the SRA has continued with 393 submissions since May 3. -![fig7](img/h5n1_cattle_outbreak_fig7_genbank_vs_sra_release_date.png) +![fig7](/blog/img/h5n1_cattle_outbreak_fig7_genbank_vs_sra_release_date.png) **Figure 7. Release dates for H5N1 genomes from the US in GenBank and in the SRA.** This [ingest pipeline](https://github.com/nextstrain/avian-flu/tree/master/ingest) is [run daily](https://github.com/nextstrain/avian-flu/actions/workflows/ingest-ncbi.yaml) and resulting sequences files are available as: @@ -102,24 +102,24 @@ and metadata available as: We provide [phylogenetic workflows](https://github.com/nextstrain/avian-flu) that analyze evolution at a few different scales. First of all, we provide a broad analysis of segment-level evolution of H5N1 viruses over the past >24 years at: -- [nextstrain.org/avian-flu/h5n1/ha/all-time](https://nextstrain.org/avian-flu/h5n1/ha/all-time) -- [nextstrain.org/avian-flu/h5n1/na/all-time](https://nextstrain.org/avian-flu/h5n1/na/all-time) +- [nextstrain.org/avian-flu/h5n1/ha/all-time](/avian-flu/h5n1/ha/all-time) +- [nextstrain.org/avian-flu/h5n1/na/all-time](/avian-flu/h5n1/na/all-time) - etc... -Notably, the currently circulating clade 2.3.4.4b viruses descend from the 2.3.4 clade of viruses that emerged [around 2004](https://nextstrain.org/avian-flu/h5nx/ha/all-time?c=subtype) and began reassorting with other NA subtypes (primarily N2, N6, and N8), forming what are collectively referred to as H5Nx viruses. +Notably, the currently circulating clade 2.3.4.4b viruses descend from the 2.3.4 clade of viruses that emerged [around 2004](/avian-flu/h5nx/ha/all-time?c=subtype) and began reassorting with other NA subtypes (primarily N2, N6, and N8), forming what are collectively referred to as H5Nx viruses. We also provide a complete evolutionary history of H5Nx since 1996 at: -- [nextstrain.org/avian-flu/h5nx/ha/all-time](https://nextstrain.org/avian-flu/h5nx/ha/all-time) -- [nextstrain.org/avian-flu/h5nx/na/all-time](https://nextstrain.org/avian-flu/h5nx/na/all-time) +- [nextstrain.org/avian-flu/h5nx/ha/all-time](/avian-flu/h5nx/ha/all-time) +- [nextstrain.org/avian-flu/h5nx/na/all-time](/avian-flu/h5nx/na/all-time) - etc... These analyses are subsampled to target subsampling equitably across space and time and so many recent sequences are not included. To compensate for this, we provide a narrower analysis of segment-level of evolution of H5N1 and H5Nx viruses over the past 2 years at: -- [nextstrain.org/avian-flu/h5n1/ha/2y](https://nextstrain.org/avian-flu/h5n1/ha/2y) -- [nextstrain.org/avian-flu/h5n1/na/2y](https://nextstrain.org/avian-flu/h5n1/na/2y) +- [nextstrain.org/avian-flu/h5n1/ha/2y](/avian-flu/h5n1/ha/2y) +- [nextstrain.org/avian-flu/h5n1/na/2y](/avian-flu/h5n1/na/2y) - etc... The targeted analysis of full genome evolution of the H5N1 cattle outbreak clade discussed above is available at: -- [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome](https://nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome) +- [nextstrain.org/avian-flu/h5n1-cattle-outbreak/genome](/avian-flu/h5n1-cattle-outbreak/genome) ## Nextclade placement of user data diff --git a/static-site/content/blog/2024-10-22-oropouche-analysis-and-resources.md b/static-site/content/blog/2024-10-22-oropouche-analysis-and-resources.md index 0b0b83541..c42e8358c 100644 --- a/static-site/content/blog/2024-10-22-oropouche-analysis-and-resources.md +++ b/static-site/content/blog/2024-10-22-oropouche-analysis-and-resources.md @@ -11,33 +11,33 @@ We are pleased to announce the availability of an Oropouche phylogenetic analysi [Oropouche virus](https://www.sciencedirect.com/science/article/pii/S016817022400011X#sec0003) is a segmented, negative-sense, single-stranded RNA virus from the Orthobunyavirus genus. It is the causative agent of Oropouche fever: an often self-limited, non-specific disease characterized by headache, arthralgia, myalgia, nausea, vomiting, chills, and/or photophobia. Oropouche virus is maintained primarily through an enzootic cycle between pale-throated sloths (_Bradypus tridactylus_), non-human primates and other wild mammals via transmission through _Culicoides paraensis_ midges, with urbanization contributing to the rise of human cases acquired through the same arthropod vector (**Fig. 1**). -[![fig1](img/oropouche_host_view.png)](https://nextstrain.org/oropouche/L?c=host) -**Figure 1. Time-resolved phylogeny for the L segment colored by the host the sample was acquired from.** 98% of sequences in our sample are human, so we have very limited information into the host reservoir dynamics. Available as [nextstrain.org/oropouche/L?c=host](https://nextstrain.org/oropouche/L?c=host). +[![fig1](/blog/img/oropouche_host_view.png)](/oropouche/L?c=host) +**Figure 1. Time-resolved phylogeny for the L segment colored by the host the sample was acquired from.** 98% of sequences in our sample are human, so we have very limited information into the host reservoir dynamics. Available as [nextstrain.org/oropouche/L?c=host](/oropouche/L?c=host). Since its original description in 1955 in Trinidad and Tobago, Oropouche virus has caused multiple local outbreaks throughout Latin America, primarily in Amazonian states of Brazil (**Fig. 2**). -[![fig2](img/oropouche_country_map.png)](https://nextstrain.org/oropouche/L) -**Figure 2. Geographic spread of Oropouche virus in the Americas.** Available as [nextstrain.org/oropouche/L](https://nextstrain.org/oropouche/L). +[![fig2](/blog/img/oropouche_country_map.png)](/oropouche/L) +**Figure 2. Geographic spread of Oropouche virus in the Americas.** Available as [nextstrain.org/oropouche/L](/oropouche/L). -Since late 2023, however, there has been a documented increase in local cases not only in Brazil but also in Cuba, Bolivia, Colombia, Peru, as well as travel-associated cases not only throughout the Americas but in Europe as well. [In 2024 alone](https://www.paho.org/en/documents/epidemiological-update-oropouche-americas-region-6-september-2024), there have been more than 9,800 confirmed cases, including two deaths. We find the majority of cases in 2023-2024 nested within the diversity of the viruses sampled in Brazil, suggesting Brazil as a main source (**Fig. 3**). Alternatively, we also see [evidence of a separate, ongoing lineage in Peru.](https://nextstrain.org/staging/oropouche/L?f_country=Peru) +Since late 2023, however, there has been a documented increase in local cases not only in Brazil but also in Cuba, Bolivia, Colombia, Peru, as well as travel-associated cases not only throughout the Americas but in Europe as well. [In 2024 alone](https://www.paho.org/en/documents/epidemiological-update-oropouche-americas-region-6-september-2024), there have been more than 9,800 confirmed cases, including two deaths. We find the majority of cases in 2023-2024 nested within the diversity of the viruses sampled in Brazil, suggesting Brazil as a main source (**Fig. 3**). Alternatively, we also see [evidence of a separate, ongoing lineage in Peru.](/oropouche/L?f_country=Peru) -[![fig3](img/oropouche_recent_seqs.png)](https://nextstrain.org/oropouche/L) -**Figure 3. L segment tree shows the 2023-2024 sequences from Brazil clustering together.** Sequences from Cuba, Colombia and Ecuador nest within the diversity of this Brazilian clade. Concurrently, we also see an ongoing lineage in Peru that is separate from the Brazilian clade. Available as [nextstrain.org/oropouche/L](https://nextstrain.org/oropouche/L). +[![fig3](/blog/img/oropouche_recent_seqs.png)](/oropouche/L) +**Figure 3. L segment tree shows the 2023-2024 sequences from Brazil clustering together.** Sequences from Cuba, Colombia and Ecuador nest within the diversity of this Brazilian clade. Concurrently, we also see an ongoing lineage in Peru that is separate from the Brazilian clade. Available as [nextstrain.org/oropouche/L](/oropouche/L). Oropouche virus has a segmented genome made up of the L (Large), M (Medium), and S (Small) segments; phylogenetic trees for these segments are constructed independently: -- [https://nextstrain.org/oropouche/L](https://nextstrain.org/oropouche/L) -- [https://nextstrain.org/oropouche/M](https://nextstrain.org/oropouche/M) -- [https://nextstrain.org/oropouche/S](https://nextstrain.org/oropouche/S) +- [https://nextstrain.org/oropouche/L](/oropouche/L) +- [https://nextstrain.org/oropouche/M](/oropouche/M) +- [https://nextstrain.org/oropouche/S](/oropouche/S) The segmented nature of Oropouche virus allows for genetic reassortment events. Similarly to [Gutierrez et al.](https://pmc.ncbi.nlm.nih.gov/articles/PMC7022353/), all three phylogenies display topographical differences, suggesting the presence of reassortment events on the genome. For example, while the phylogenies of the L and M segments show more robust clustering of the sequences from Brazil throughout time, the S segment phylogeny shows the Brazil sequences scattered throughout the tree (**Fig. 4**). If we focus on the most recent sequences from 2022-2024, however, we do find more robust clustering of the sequences from Brazil, similar to the results presented by [Naveca et al](https://www.nature.com/articles/s41591-024-03300-3). -[![fig4](img/oropouche_s_and_m_segment.png)](https://nextstrain.org/oropouche/M) -**Figure 4. Comparison of the M (top) and S (bottom) segment trees shows distinct topographical differences, suggesting the presence of reassortment events.** Available as [nextstrain.org/oropouche/M](https://nextstrain.org/oropouche/M) and [nextstrain.org/oropouche/S](https://nextstrain.org/oropouche/S) respectively. +[![fig4](/blog/img/oropouche_s_and_m_segment.png)](/oropouche/M) +**Figure 4. Comparison of the M (top) and S (bottom) segment trees shows distinct topographical differences, suggesting the presence of reassortment events.** Available as [nextstrain.org/oropouche/M](/oropouche/M) and [nextstrain.org/oropouche/S](/oropouche/S) respectively. We can track the co-evolution of the Oropouche segments via tanglegrams. For example, if we focus on the L and M segments (**Fig. 5**), we can see evidence of at least two recombination events. -[![fig5](img/oropouche_tanglegram.png)](https://nextstrain.org/oropouche/L:oropouche/M) -**Figure 5. Tanglegram of L and M segments.** Available as [nextstrain.org/oropouche/L:oropouche/M](https://nextstrain.org/oropouche/L:oropouche/M). +[![fig5](/blog/img/oropouche_tanglegram.png)](/oropouche/L:oropouche/M) +**Figure 5. Tanglegram of L and M segments.** Available as [nextstrain.org/oropouche/L:oropouche/M](/oropouche/L:oropouche/M). # Nextstrain resources diff --git a/static-site/pages/blog.jsx b/static-site/pages/blog.jsx deleted file mode 100644 index a02307bee..000000000 --- a/static-site/pages/blog.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useRouter } from "next/router"; -import { useEffect} from 'react'; -import { getBlogPosts } from "../src/util/blogPosts"; - - -/** - * Default export is a component which immediately redirects to the latest blog - * post. Note that we could achieve the same end by adding redirects to - * `next.config.mjs` but we run into issues (performance & technical) when - * running getBlogPosts there. - */ -export default function Index({redirectTo}) { - const router = useRouter(); - useEffect(() => { - if (redirectTo) router.replace(redirectTo); - }, [redirectTo, router]) - return null; -} - -/** - * Return the URL for the latest blog post - * - * Note that we cannot return a redirect property here as that's not - * useable for statically generated sites. See - * - */ -export async function getStaticProps() { - const latestPost = getBlogPosts()[0]; - return { - props: {redirectTo: `/blog/${latestPost.blogUrlName}`} - } -} diff --git a/static-site/pages/blog/[id].jsx b/static-site/pages/blog/[id].jsx deleted file mode 100644 index 859f0de2f..000000000 --- a/static-site/pages/blog/[id].jsx +++ /dev/null @@ -1,47 +0,0 @@ -import dynamic from 'next/dynamic' -import { getBlogPosts } from "../../src/util/blogPosts"; -const DisplayMarkdown = dynamic(() => import("../../src/templates/displayMarkdown"), {ssr: false}) - - -/** - * Generate a list of the appropriate blog URLs we want Next.JS to generate - * static pages for. - * - * Note that the docs - * - * indicate you can export arbitrary data from here and consume it in - * getStaticProps but the only field you can pass is the router param ("id" in - * this case). See - */ -export const getStaticPaths = (async () => { - const posts = getBlogPosts(); - const paths = posts.map((post) => ({params: { - id: post.blogUrlName, /* matches the `id` router param defined via `[id].jsx` */ - }})); - return { - paths, - fallback: false // any paths not defined will result in a 404 page - } -}) - -/** - * For a given id (matching the route /blog/id via the dynamic routing filename `blog/[id].jsx`) - * return the params to be parsed to the rendering component. - * - * Note: The context.params.id is set to one of the entries in the array returned from - * `getStaticPaths`. See - */ -export const getStaticProps = (async (context) => { - const posts = getBlogPosts(); - const thisPost = posts.find((post) => post.blogUrlName===context.params.id); - const sidebarData = posts.map((post) => { - return {date: post.date, blogUrlName: post.blogUrlName, sidebarTitle: post.sidebarTitle, selected: post===thisPost}; - }) - return { - props: {...thisPost, sidebarData} - }; -}) - -export default function Index(props) { - return () -} diff --git a/static-site/src/templates/displayMarkdown.jsx b/static-site/src/templates/displayMarkdown.jsx deleted file mode 100644 index 3ff652dc9..000000000 --- a/static-site/src/templates/displayMarkdown.jsx +++ /dev/null @@ -1,189 +0,0 @@ -import React from "react"; -import Head from "next/head"; -import styled from "styled-components"; -import SEO from "../components/SEO/SEO"; -import NavBar from '../components/nav-bar'; -import Sidebar from "../components/Sidebar"; -import { CenteredContainer, MarkdownContent } from "../layouts/generalComponents"; -import UserDataWrapper from "../layouts/userDataWrapper"; -import Footer from "../components/Footer"; -import MainLayout from "../components/layout"; -import { parseMarkdown } from "../util/parseMarkdown"; - -export default class GenericTemplate extends React.Component { - constructor(props) { - super(props); - this.toggleSidebar = this.toggleSidebar.bind(this); - this.state = {mql: undefined, sidebarOpen: undefined, mobileDisplay: undefined}; - } - componentDidMount() { - /* window listener to see when width changes cross threshold to toggle sidebar */ - /* can't be in the constructor -- https://github.com/gatsbyjs/gatsby/issues/309 */ - const mql = window.matchMedia(`(min-width: 780px)`); - mql.addListener(() => this.setState({ - sidebarOpen: this.state.mql.matches, - mobileDisplay: !this.state.mql.matches - })); - this.setState({ - mql, - sidebarOpen: mql.matches, - mobileDisplay: !mql.matches - }); - } - toggleSidebar() { - this.setState({ - sidebarOpen: !this.state.sidebarOpen - }); - } - // - renderMobileTogglesAndShading() { - const iconSliders = ( - - - - ); - const iconX = ( - - - - ); - return ( -
- - - {this.state.sidebarOpen ? iconX : iconSliders} - - - -
- ); - } - - render() { - const {author, date, title, blogUrlName, mdstring, sidebarData} = this.props; - /* create a description for SEO from the blog metadata */ - const description = `Nextstrain blog post from ${date}; author(s): ${author}` - return ( - - - {title} - - - - - - - - - - - - {date} - {title} - {author} - - - -