Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Author page alternate template (topics-focused) #3585

Merged
merged 9 commits into from
May 17, 2024
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