diff options
author | Fushihara <1039534+fushihara@users.noreply.github.com> | 2024-09-23 15:19:25 +0900 |
---|---|---|
committer | Fushihara <1039534+fushihara@users.noreply.github.com> | 2024-09-23 15:19:25 +0900 |
commit | ba0c8d73e585d7ff249dd90684254bc7fa025544 (patch) | |
tree | 3b35be04e7bf11eb046246696d45fb8854f01f14 /src/util/articleLoader.ts | |
parent | 41eff0687f69539a514cfa3a28a68f89fc8c54fd (diff) |
commit
Diffstat (limited to 'src/util/articleLoader.ts')
-rw-r--r-- | src/util/articleLoader.ts | 88 |
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; + } +} |