summaryrefslogtreecommitdiff
path: root/src/util/articleLoader.ts
diff options
context:
space:
mode:
authorFushihara <1039534+fushihara@users.noreply.github.com>2024-09-23 15:19:25 +0900
committerFushihara <1039534+fushihara@users.noreply.github.com>2024-09-23 15:19:25 +0900
commitba0c8d73e585d7ff249dd90684254bc7fa025544 (patch)
tree3b35be04e7bf11eb046246696d45fb8854f01f14 /src/util/articleLoader.ts
parent41eff0687f69539a514cfa3a28a68f89fc8c54fd (diff)
commit
Diffstat (limited to 'src/util/articleLoader.ts')
-rw-r--r--src/util/articleLoader.ts88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/util/articleLoader.ts b/src/util/articleLoader.ts
new file mode 100644
index 0000000..787725a
--- /dev/null
+++ b/src/util/articleLoader.ts
@@ -0,0 +1,88 @@
+import { readFile } from "fs/promises";
+import { z } from "zod";
+const zodType = z.array(
+ z.object({
+ articleId: z.number().int().min(0),
+ title: z.string(),
+ timestampMs: z.number().int().min(0),
+ maxPageNumber: z.number().int().min(1),
+ tags: z.array(z.string()),
+ breadLinks: z.array(z.string()).min(1),
+ })
+);
+const MAX_ITEM_LIMIT = process.env["AKIBA_SOUKEN_MAX_ITEM_LIMIT"];
+export class ArticleLoader {
+ constructor() { }
+ async loadData() {
+ const articleJsonPath = process.env["AKIBA_SOUKEN_ARTICLE_JSON"]!;
+ //console.log(`[${articleJsonPath}]`);
+ const jsonStr = await readFile(articleJsonPath, { encoding: "utf-8" }).then(text => {
+ const jsonObj = JSON.parse(text);
+ return jsonObj;
+ });
+ const parsedObj = zodType.parse(jsonStr);
+ parsedObj.sort((a, b) => {
+ return b.timestampMs - a.timestampMs;
+ });
+ if (MAX_ITEM_LIMIT != null) {
+ parsedObj.length = Math.min(Number(MAX_ITEM_LIMIT), parsedObj.length);
+ }
+ return parsedObj;
+ }
+ async getCategoryList() {
+ const loadedData = await this.loadData();
+ // key:カテゴリ名 , val:回数
+ const categoryCount = new Map<string, number>();
+ for (const a of loadedData) {
+ if (a.breadLinks.length == 0) {
+ continue;
+ }
+ const category = a.breadLinks[0];
+ if (categoryCount.has(category)) {
+ categoryCount.set(category, categoryCount.get(category)! + 1);
+ } else {
+ categoryCount.set(category, 1);
+ }
+ }
+ const categoryList: { name: string, count: number }[] = [];
+ for (const [name, count] of categoryCount) {
+ categoryList.push({ name: name, count });
+ }
+ categoryList.sort((a, b) => {
+ return b.count - a.count;
+ });
+ return categoryList;
+ }
+ async getTagList() {
+ const loadedData = await this.loadData();
+ // key:タグ名 , val:回数
+ const tagCount = new Map<string, number>();
+ for (const a of loadedData) {
+ const tags = new Set<string>();
+ a.tags.forEach(t => {
+ tags.add(t);
+ });
+ // パンくずリストの2件目以降はタグ扱いになっている
+ a.breadLinks.forEach((b, index) => {
+ if (index != 0) {
+ tags.add(b);
+ }
+ })
+ for (const tag of tags) {
+ if (tagCount.has(tag)) {
+ tagCount.set(tag, tagCount.get(tag)! + 1);
+ } else {
+ tagCount.set(tag, 1);
+ }
+ }
+ }
+ const tagList: { name: string, count: number }[] = [];
+ for (const [name, count] of tagCount) {
+ tagList.push({ name: name, count });
+ }
+ tagList.sort((a, b) => {
+ return b.count - a.count;
+ });
+ return tagList;
+ }
+}