Skip to content

Commit

Permalink
Author page alternate template (topics-focused) (#3585)
Browse files Browse the repository at this point in the history
Added a route to get an ArchieML output of all work produced by an author from both gdoc articles and wordpress pages.

- Example route: http://staging-site-author-page-alt/admin/api/all-work?author=Esteban%20Ortiz-Ospina 
- Example page: http://staging-site-author-page-alt/admin/gdocs/1pU8nVBchpI_LYZmNOo4Dag9lHvQkzG-r8JD_57_-1rw/preview

### Why make this change?

To provide a way to manually populate the secondary section of research and writing blocks on author pages with topics rather than articles. This is relevant for a handful of authors who mostly work on topic pages, as well as data managers.

![Screenshot 2024-05-07 at 14.31.06.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/0SFFiIjKuUK6UPYHVe6u/16ed5a76-2cef-4b0e-aa0e-8dcbea95ac73.png)
  • Loading branch information
mlbrgl authored May 17, 2024
2 parents 0258fee + e5edd00 commit eb2966e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 41 deletions.
111 changes: 111 additions & 0 deletions adminSiteServer/apiRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ import { match } from "ts-pattern"
import { GdocDataInsight } from "../db/model/Gdoc/GdocDataInsight.js"
import { GdocHomepage } from "../db/model/Gdoc/GdocHomepage.js"
import { GdocAuthor } from "../db/model/Gdoc/GdocAuthor.js"
import path from "path"

const apiRouter = new FunctionalRouter()

Expand Down Expand Up @@ -2543,4 +2544,114 @@ deleteRouteWithRWTransaction(
}
)

// Get an ArchieML output of all the work produced by an author. This includes
// gdoc articles, gdoc modular/linear topic pages and wordpress modular topic
// pages. Data insights are excluded. This is used to manually populate the
// [.secondary] section of the {.research-and-writing} block of author pages
// using the alternate template, which highlights topics rather than articles.
getRouteWithROTransaction(apiRouter, "/all-work", async (req, res, trx) => {
type WordpressPageRecord = {
isWordpressPage: number
} & Record<
"slug" | "title" | "subtitle" | "thumbnail" | "authors" | "publishedAt",
string
>
type GdocRecord = Pick<DbRawPostGdoc, "id" | "publishedAt">

const author = req.query.author || "Max Roser"
const gdocs = await db.knexRaw<GdocRecord>(
trx,
`-- sql
SELECT id, publishedAt
FROM posts_gdocs
WHERE JSON_CONTAINS(content->'$.authors', '"${author}"')
AND type NOT IN ("data-insight", "fragment")
AND published = 1
`
)

// type: page
const wpModularTopicPages = await db.knexRaw<WordpressPageRecord>(
trx,
`-- sql
SELECT
wpApiSnapshot->>"$.slug" as slug,
wpApiSnapshot->>"$.title.rendered" as title,
wpApiSnapshot->>"$.excerpt.rendered" as subtitle,
TRUE as isWordpressPage,
wpApiSnapshot->>"$.authors_name" as authors,
wpApiSnapshot->>"$.featured_media_paths.medium_large" as thumbnail,
wpApiSnapshot->>"$.date" as publishedAt
FROM posts p
WHERE wpApiSnapshot->>"$.content" LIKE '%topic-page%'
AND JSON_CONTAINS(wpApiSnapshot->'$.authors_name', '"${author}"')
AND wpApiSnapshot->>"$.status" = 'publish'
AND NOT EXISTS (
SELECT 1 FROM posts_gdocs pg
WHERE pg.slug = p.slug
AND pg.content->>'$.type' LIKE '%topic-page'
)
`
)

const isWordpressPage = (
post: WordpressPageRecord | GdocRecord
): post is WordpressPageRecord =>
(post as WordpressPageRecord).isWordpressPage === 1

function* generateProperty(key: string, value: string) {
yield `${key}: ${value}\n`
}

const sortByDateDesc = (
a: GdocRecord | WordpressPageRecord,
b: GdocRecord | WordpressPageRecord
): number => {
if (!a.publishedAt || !b.publishedAt) return 0
return (
new Date(b.publishedAt).getTime() -
new Date(a.publishedAt).getTime()
)
}

function* generateAllWorkArchieMl() {
for (const post of [...gdocs, ...wpModularTopicPages].sort(
sortByDateDesc
)) {
if (isWordpressPage(post)) {
yield* generateProperty(
"url",
`https://ourworldindata.org/${post.slug}`
)
yield* generateProperty("title", post.title)
yield* generateProperty("subtitle", post.subtitle)
yield* generateProperty(
"authors",
JSON.parse(post.authors).join(", ")
)
const parsedPath = path.parse(post.thumbnail)
yield* generateProperty(
"filename",
// /app/uploads/2021/09/reducing-fertilizer-768x301.png -> reducing-fertilizer.png
path.format({
name: parsedPath.name.replace(/-\d+x\d+$/, ""),
ext: parsedPath.ext,
})
)
yield "\n"
} else {
// this is a gdoc
yield* generateProperty(
"url",
`https://docs.google.com/document/d/${post.id}/edit`
)
yield "\n"
}
}
}

res.type("text/plain")
res.send([...generateAllWorkArchieMl()].join(""))
})

export { apiRouter }
38 changes: 1 addition & 37 deletions db/model/Gdoc/rawToEnriched.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1636,40 +1636,6 @@ function parseExpandableParagraph(
function parseResearchAndWritingBlock(
raw: RawBlockResearchAndWriting
): EnrichedBlockResearchAndWriting {
const createError = (
error: ParseError,
heading = "",
hideAuthors = false,
primary = [
{
value: { url: "" },
},
],
secondary = [
{
value: { url: "" },
},
],
more: EnrichedBlockResearchAndWritingRow = {
heading: "",
articles: [],
},
latest: EnrichedBlockResearchAndWritingRow = {
heading: "",
articles: [],
},
rows: EnrichedBlockResearchAndWritingRow[] = []
): EnrichedBlockResearchAndWriting => ({
type: "research-and-writing",
heading,
"hide-authors": hideAuthors,
primary,
secondary,
more,
latest,
rows,
parseErrors: [error],
})
const parseErrors: ParseError[] = []

function enrichLink(
Expand Down Expand Up @@ -1729,12 +1695,10 @@ function parseResearchAndWritingBlock(
})
}

if (!raw.value.primary)
return createError({ message: "Missing primary link" })
const primary: EnrichedBlockResearchAndWritingLink[] = []
if (isArray(raw.value.primary)) {
primary.push(...raw.value.primary.map((link) => enrichLink(link)))
} else {
} else if (raw.value.primary) {
primary.push(enrichLink(raw.value.primary))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,8 @@ export enum SocialLinkType {
}

export type RawSocialLink = {
text: string
url: string
text?: string
url?: string
type?: SocialLinkType
}

Expand All @@ -862,7 +862,11 @@ export type RawBlockSocials = {
value: RawSocialLink[] | ArchieMLUnexpectedNonObjectValue
}

export type EnrichedSocialLink = RawSocialLink
export type EnrichedSocialLink = {
text: string
url: string
type?: SocialLinkType
}

export type EnrichedBlockSocials = {
type: "socials"
Expand Down
2 changes: 1 addition & 1 deletion packages/@ourworldindata/utils/src/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function getFilenameWithoutExtension(
export function getFilenameExtension(
filename: ImageMetadata["filename"]
): string {
return filename.slice(filename.indexOf(".") + 1)
return filename.slice(filename.lastIndexOf(".") + 1)
}

export function getFilenameAsPng(filename: ImageMetadata["filename"]): string {
Expand Down

0 comments on commit eb2966e

Please sign in to comment.