Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/tags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getParentTags,
getChildTags,
getSubTags,
getTagsWithSameMeaning,
} from "./page-getters";
import { getTagId, getTagNameFromFeed } from "./works-feed-getters";
import {
Expand Down Expand Up @@ -79,6 +80,7 @@ export const getTag = async ({
parentTags: getParentTags(tagPage),
childTags: getChildTags(tagPage),
subTags: getSubTags(tagPage),
tagsWithSameMeaning: getTagsWithSameMeaning(tagPage),
};
};

Expand Down
8 changes: 8 additions & 0 deletions src/tags/page-getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ export const getSubTags = ($tagPage: TagPage) => {
});
return subTags;
};

export const getTagsWithSameMeaning = ($tagPage: TagPage) => {
const tagsWithSameMeaning: string[] = [];
$tagPage(".synonym ul.tags li").each((_, element) => {
tagsWithSameMeaning.push($tagPage(element).text());
});
return tagsWithSameMeaning;
};
28 changes: 17 additions & 11 deletions src/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const getAsShortUrl = ({ url }: { url: string | URL }) => {
const longUrl = new URL(url);
if (longUrl.hostname !== "archiveofourown.org") {
throw new Error(
`Short URLs are only supported for AO3 (found: ${longUrl.hostname})`
`Short URLs are only supported for AO3 (found: ${longUrl.hostname})`,
);
}

Expand All @@ -99,8 +99,8 @@ export const getDownloadUrls = ({
updatedAt,
publishedAt,
}: // Make it so you can either pass specifically the needed elements of a work,
// but also the whole summary if you prefer
| Pick<WorkSummary, "id" | "title" | "updatedAt" | "publishedAt">
// but also the whole summary if you prefer
| Pick<WorkSummary, "id" | "title" | "updatedAt" | "publishedAt">
| WorkSummary) => {
const timestamp = new Date(updatedAt ?? publishedAt).valueOf();
const downloadLinkBase = new URL(`downloads/${id}/`, getArchiveBaseUrl())
Expand Down Expand Up @@ -130,7 +130,7 @@ const TOKEN_REPLACEMENTS_MAP = {
type ReplaceableToken = keyof typeof TOKEN_REPLACEMENTS_MAP;

const REPLACEABLE_TOKENS = Object.keys(
TOKEN_REPLACEMENTS_MAP
TOKEN_REPLACEMENTS_MAP,
) as ReplaceableToken[];

const TOKENS_TO_ESCAPE = ["/", "?", "."];
Expand All @@ -145,19 +145,19 @@ const isReplaceableToken = (c: string): c is ReplaceableToken =>
*/
const REPLACE_TOKENS_REGEX = new RegExp(
`(${REPLACEABLE_TOKENS.map((token) =>
shouldEscapeToken(token) ? `\\${token}` : token
shouldEscapeToken(token) ? `\\${token}` : token,
).join("|")})`,
"g"
"g",
);

export const getTagUrl = (tagName: string) =>
new URL(
`tags/${encodeURI(tagName).replaceAll(
REPLACE_TOKENS_REGEX,
(char: string) =>
isReplaceableToken(char) ? TOKEN_REPLACEMENTS_MAP[char] : char
isReplaceableToken(char) ? TOKEN_REPLACEMENTS_MAP[char] : char,
)}/`,
getArchiveBaseUrl()
getArchiveBaseUrl(),
).href;

export const getTagWorksFeedUrl = (tagName: string) =>
Expand Down Expand Up @@ -198,15 +198,21 @@ export const getWorkDetailsFromUrl = ({
};

const getSearchParamsFromTagFilters = (
searchFilters: Partial<TagSearchFilters>
searchFilters: Partial<TagSearchFilters>,
) => {
// Prepare the parameters for the search as a map first. This makes them a bit
// more readable, since these parameters will all need to be wrapped with with
// "tag_search[]" in the URL.
const parameters = {
name: searchFilters.tagName ?? "",
fandoms: searchFilters.fandoms?.join(",") ?? "",
type: searchFilters.type?.toLowerCase() ?? "",
// AO3 requires an empty string for "any" type
// This is not the same for wrangling_status, somehow
type:
searchFilters.type && searchFilters.type !== "any"
? searchFilters.type.charAt(0).toUpperCase() +
searchFilters.type.slice(1).toLowerCase()
: "",
wrangling_status:
searchFilters.wranglingStatus
// We remove the _or_ and _and_ that we added for readability
Expand All @@ -216,7 +222,7 @@ const getSearchParamsFromTagFilters = (
sort_column:
searchFilters.sortColumn === "works_count"
? "uses"
: searchFilters.sortColumn ?? "name",
: (searchFilters.sortColumn ?? "name"),
sort_direction: searchFilters.sortDirection ?? "asc",
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<script src="/javascripts/livevalidation_standalone.js"></script>

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="yxRh6hLrETaW5JOqwgTYm9r_LeLtUw3h4AyA5ARWeYK1MrissBOsGvDLFf7beoZ8nSsYOOOjS55-DGWdAKWnvw" />
<meta name="csrf-token" content="iUZc6Oy81BpMSZjWkbnmjS2jwWiozFk8M-GnwKeWiPIbjbx1Ppn5qFeX2KU6JBZ-zbDIzqIEH-BveMnoxwGsEA" />


</head>
Expand All @@ -57,7 +57,7 @@ <h1 class="heading">
<a id="login-dropdown" href="/users/login?return_to=%2Ftags%2Fsearch%3Fview_adult%3Dtrue%26commit%3DSearch%2BTags%26page%3D3%26tag_search%255Bfandoms%255D%3D%26tag_search%255Bname%255D%3Dan%2Bunusual%26tag_search%255Bsort_column%255D%3Dname%26tag_search%255Bsort_direction%255D%3Dasc%26tag_search%255Btype%255D%3D%26tag_search%255Bwrangling_status%255D%3D">Log In</a>
</p>
<div id="small_login" class="simple login">
<form class="new_user" id="new_user_session_small" action="/users/login?return_to=%2Ftags%2Fsearch%3Fview_adult%3Dtrue%26commit%3DSearch%2BTags%26page%3D3%26tag_search%255Bfandoms%255D%3D%26tag_search%255Bname%255D%3Dan%2Bunusual%26tag_search%255Bsort_column%255D%3Dname%26tag_search%255Bsort_direction%255D%3Dasc%26tag_search%255Btype%255D%3D%26tag_search%255Bwrangling_status%255D%3D" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="OluH3WWT0UtzsoPlYl3ZarStKywsfrLGpItR9ovylHUxPuc6J7kaHbFJrLfw4jo6KiOL80bcwlsJwc3vb0ufsA" autocomplete="off" />
<form class="new_user" id="new_user_session_small" action="/users/login?return_to=%2Ftags%2Fsearch%3Fview_adult%3Dtrue%26commit%3DSearch%2BTags%26page%3D3%26tag_search%255Bfandoms%255D%3D%26tag_search%255Bname%255D%3Dan%2Bunusual%26tag_search%255Bsort_column%255D%3Dname%26tag_search%255Bsort_direction%255D%3Dasc%26tag_search%255Btype%255D%3D%26tag_search%255Bwrangling_status%255D%3D" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="1DXb8emFRE9DTJ7n80gWlkZmCImvYoPKjiUKfS229Gy0NhKzCxfNAvrMnalFpr_826hM-_0NAh7Xmq-4AUg5BA" autocomplete="off" />
<dl>
<dt><label for="user_session_login_small">Username or email:</label></dt>
<dd><input autocomplete="on" id="user_session_login_small" type="text" name="user[login]" /></dd>
Expand Down Expand Up @@ -138,7 +138,7 @@ <h1 class="heading">
<p>
<label class="landmark" for="site_search">Work Search</label>
<input class="text" id="site_search" aria-describedby="site_search_tooltip" type="text" name="work_search[query]" />
<span class="tip" role="tooltip" id="site_search_tooltip">tip: &quot;sherlock (tv)&quot; m/m NOT &quot;sherlock holmes/john watson&quot;</span>
<span class="tip" role="tooltip" id="site_search_tooltip">tip: lex m/m (mature OR explicit)</span>
<span class="submit actions"><input type="submit" value="Search" class="button" /></span>
</p>
</fieldset>
Expand Down Expand Up @@ -357,7 +357,7 @@ <h4 class="heading">Contact Us</h4>
<h4 class="heading">Development</h4>
<ul class="menu">
<li>
<a href="https://github.com/otwcode/otwarchive/commits/v0.9.452.1">otwarchive v0.9.452.1</a>
<a href="https://github.com/otwcode/otwarchive/commits/v0.9.453.2">otwarchive v0.9.453.2</a>
</li>
<li><a href="/known_issues">Known Issues</a></li>
<li>
Expand Down
342 changes: 342 additions & 0 deletions tests/mocks/data/ao3/tags/Charlie Magne ! Morningstar/index.html

Large diffs are not rendered by default.

2,869 changes: 2,869 additions & 0 deletions tests/mocks/data/ao3/tags/Charlie Magne ! Morningstar/works.html

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion tests/tag-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ describe("Tags/search", () => {
// Some tags we should find to make sure the parser is working correctly.
const UNSORTED_TAG = "it was an unusual premise for this fandom that is";
const FREEFORM_TAG = 'even an unusual variation on "Just The Tip"';
const CHARACTER_TAG = "Goddess Who Decides to Answer an Unusual Prayer (Original Work)";
const CHARACTER_TAG =
"Goddess Who Decides to Answer an Unusual Prayer (Original Work)";
const CANONICAL_TAG =
"Episode: s02e23 Facing an Unusual Past (Saiki Kusuo no Sai-nan)";

Expand Down
132 changes: 132 additions & 0 deletions tests/tags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,135 @@ describe("Tags/sub", () => {
expect(tag).toMatchObject({ name: "Eventual Romance", subTags: [] });
});
});

describe("Tags/synonyms", () => {
it("should fetch tags with same meaning for canonical tags", async () => {
const tag = await getTag({ tagName: "Charlie Magne | Morningstar" });

expect(tag.name).toBe("Charlie Magne | Morningstar");
expect(tag.canonical).toBe(true);
expect(tag.tagsWithSameMeaning).toMatchInlineSnapshot(`
[
"(Charlie just mentioned)",
"(mentioned) Charlie Magne | Morningstar",
"(mentions) Charlie Magne",
"2p Charlie (Mentioned briefly)",
"2P Charlie - Character",
"2P Charlie Magne",
"Anti-Charlie",
"baby Charlie Magne - Character",
"Baby Charlie Morningstar - Character",
"Background Charlie - Character",
"Background Charlie Magne | Morningstar",
"Blessed Cat Charlie",
"Brief Charlie Morningstar - Character",
"cahrlie",
"Charlie (briefly mentioned)",
"Charlie (Hazbin Hotel)",
"Charlie (Hazbin Hotel) (mentioned)",
"Charlie (Hazbin Hotel) | mentioned",
"Charlie (Hazbin Hotel)(background)",
"Charlie (Hazbing Hotel)",
"Charlie (Mentioned small cameo)",
"Charlie - Mentioned",
"Charlie Hazbin hotel",
"Charlie Hellspawn",
"charlie is here but not enough to tag really",
"Charlie Killjoy",
"Charlie Magne",
"Charlie Magne (Deceased)",
"Charlie Magne (Hazbin Hotel)",
"Charlie Magne (Mention Only)",
"Charlie Magne (mentioned)",
"Charlie Magne (Referenced)",
"Charlie Magne [mentioned]",
"Charlie Magne Morningstar",
"Charlie magne | morningstar ( mentioned )",
"Charlie Magne | Morningstar (Briefly)",
"Charlie Magne | Morningstar (Hazbin Hotel) (mentioned)",
"Charlie Magne | Morningstar (Mention)",
"Charlie Magne | Morningstar (mentioned)",
"Charlie Magne | Morningstar (Minor)",
"Charlie Magne | Morningstar (referenced)",
"Charlie Magne | Morningstar - mentioned",
"Charlie Magne | Morningstar mentioned",
"Charlie Magne | Morningstar ×",
"Charlie Magne | Morningstar(but only mentioned)",
"Charlie Magne | Morningstar(mentioned)",
"Charlie Magne-Morningstar",
"Charlie Magnet",
"Charlie Magne| Morningstar (Hazbin Hotel)",
"Charlie Mange",
"Charlie Mange | Morningstar",
"Charlie Mange | Morningstar (Hazbin Hotel)",
"Charlie mentioned (Hazbin Hotel)",
"Charlie Mor",
"Charlie Moringstar",
"Charlie Morningstar",
"Charlie morningstar (hasbin hotel)",
"Charlie Morningstar (Hazbin Hotel)",
"Charlie Morningstar (Hazbin Hotel) (mentioned)",
"Charlie Morningstar (mentioned)",
"Charlie Morningstar - Mentioned",
"Charlie Morningstar implied",
"Charlie Morningstar(Hazbin Hotel)",
"Charlie Morningstar/Magne",
"charlie's mentioned near the end",
"Charlie's shadow",
"charlie( hazbin hotel)",
"Charlie(Hazbin Hotel)",
"Charlotte "Charlie" Magne | Morningstar",
"Charlotte "Charlie" Morningstar",
"Charlotte Magne",
"Charlotte Magne (Hazbin Hotel)",
"Charlotte Morningstar",
"Charlotte Morningstar (Hazbin Hotel)",
"Charlotte vasiliou magne (hazbin hotel au)",
"Charolette Magne (Hazbin Hotel)",
"child Charlie - Character",
"Cursed Cat Charlie",
"Dark Charlie Magne | Morningstar - Character",
"Dark!Queen Charlie",
"Demeter Charlie",
"Demon Charlie - Character",
"Demon Charlie Magne - Character",
"Demon Charlie Morningstar",
"Demon!Charlie - Character",
"Evil!Charlie - Character",
"Human Charlie - Character",
"Human Charlie Magne (Hazbin Hotel)",
"human!Charlie - Character",
"kid Charlie (Hazbin Hotel)",
"Mention of Charlie Morningstar (Hazbin Hotel)",
"mentioned Charlie",
"Mentioned Charlie (Hazbin Hotel)",
"mentioned Charlie Magne - Character",
"Mentioned Charlie Magne Morningstar",
"mentioned Charlie Magne | Morningstar - Character",
"Mentioned Charlie mange",
"Mentioned Charlie Morningstar",
"Mentioned Charlie Morningstar (Hazbin hotel) - Character",
"Mentions Charlie Magne | Morningstar",
"mentions of Charlie Magne",
"Mentions of Charlie Magne | Morningstar",
"Mentions of Charlie Morningstar - Character",
"Minor Charlie Magne | Morningstar - Character",
"Princess Charlie (Hazbin Hotel)",
"Princess Charlotte Morningstar - Character",
"Referenced Charlie",
"swap charlie",
"The Savant | Charlie Morningstar",
]
`);
});

it("should return empty array for tags with no synonyms", async () => {
const tag = await getTag({ tagName: "Original Senator Characters" });

expect(tag).toMatchObject({
name: "Original Senator Characters",
tagsWithSameMeaning: [],
});
});
});

Loading
Loading