From 8ba3367fbe097c0e750f2f89fba319e345b4123a Mon Sep 17 00:00:00 2001 From: ToBinio <81473300+ToBinio@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:19:14 +0200 Subject: [PATCH] feat: mod counts (#2) --- components/Charts.vue | 67 ++++++++++++++++++++++++ components/navigation/DropDownLink.vue | 58 +++++++++++++++++++++ layouts/default.vue | 56 ++++++-------------- pages/counts/[mode].vue | 8 +++ pages/downloads/[mode].vue | 71 ++------------------------ server/api/counts.ts | 6 +++ server/api/downloads.ts | 49 ++---------------- server/utils/api/stats.ts | 50 ++++++++++++++++++ server/utils/statistics.ts | 67 ++++++++++++++---------- server/utils/types/downloads.ts | 7 --- server/utils/types/stats.ts | 9 ++++ 11 files changed, 260 insertions(+), 188 deletions(-) create mode 100644 components/Charts.vue create mode 100644 components/navigation/DropDownLink.vue create mode 100644 pages/counts/[mode].vue create mode 100644 server/api/counts.ts create mode 100644 server/utils/api/stats.ts delete mode 100644 server/utils/types/downloads.ts create mode 100644 server/utils/types/stats.ts diff --git a/components/Charts.vue b/components/Charts.vue new file mode 100644 index 0000000..d15a7ca --- /dev/null +++ b/components/Charts.vue @@ -0,0 +1,67 @@ + + + + + + \ No newline at end of file diff --git a/components/navigation/DropDownLink.vue b/components/navigation/DropDownLink.vue new file mode 100644 index 0000000..30e531d --- /dev/null +++ b/components/navigation/DropDownLink.vue @@ -0,0 +1,58 @@ + + + + + {{ title }} + + + {{ route.title }} + + + + + + \ No newline at end of file diff --git a/layouts/default.vue b/layouts/default.vue index e2ef34b..c40dc2a 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,27 +1,22 @@ @@ -29,15 +24,9 @@ watch(currentUrl, (url) => { Modrinth Statistics - - - Downloads - - - {{ download.title }} - - - + + + update Data @@ -65,26 +54,13 @@ watch(currentUrl, (url) => { font-size: xx-large; } - button { - color: black; - } - - #downloads { + #links { display: flex; - flex-direction: column; - - .header { - transition: 0.2s color ease-out; - font-size: x-large; - } - - select { - background-color: transparent; - border: none; - border-radius: 7px; + gap: 25px; + } - padding: 0 0 5px; - } + button { + color: black; } a { diff --git a/pages/counts/[mode].vue b/pages/counts/[mode].vue new file mode 100644 index 0000000..e20a28c --- /dev/null +++ b/pages/counts/[mode].vue @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/pages/downloads/[mode].vue b/pages/downloads/[mode].vue index adefba7..217dd23 100644 --- a/pages/downloads/[mode].vue +++ b/pages/downloads/[mode].vue @@ -1,74 +1,9 @@ - - - \ No newline at end of file + + \ No newline at end of file diff --git a/server/api/counts.ts b/server/api/counts.ts new file mode 100644 index 0000000..e22aede --- /dev/null +++ b/server/api/counts.ts @@ -0,0 +1,6 @@ +import {exportStats, StatExport} from "~/server/utils/api/stats"; + +export default defineEventHandler(async (event): Promise => { + return exportStats(event, value => value.count) +}) + diff --git a/server/api/downloads.ts b/server/api/downloads.ts index 7e6cf2d..f04e29a 100644 --- a/server/api/downloads.ts +++ b/server/api/downloads.ts @@ -1,48 +1,7 @@ -import {Downloads} from "~/server/utils/types/downloads"; +import {Stats} from "~/server/utils/types/stats"; +import {exportStats, StatExport} from "~/server/utils/api/stats"; -type result = { - labels: string[] - data: { - name: string - color: string, - downloads: number[] - }[] -} - -export default defineEventHandler(async (event): Promise => { - const storage = useStorage("statistics"); - let query = getQuery(event); - - let downloads - - switch (query.mode) { - case "minor" : - downloads = await storage.getItem("minorVersionDownloads") - break - case "all" : - downloads = await storage.getItem("allVersionDownloads") - break - default: - downloads = await storage.getItem("majorVersionDownloads") - } - - if (!downloads) { - // todo - error message - return { - labels: [], - data: [] - } - } - - return { - labels: downloads.versions, - data: downloads.data.map(value => { - return { - name: value.name, - color: getColorForLauncher(value.name), - downloads: value.downloads - } - }) - } +export default defineEventHandler(async (event): Promise => { + return exportStats(event, value => value.count) }) diff --git a/server/utils/api/stats.ts b/server/utils/api/stats.ts new file mode 100644 index 0000000..df4dab3 --- /dev/null +++ b/server/utils/api/stats.ts @@ -0,0 +1,50 @@ +import {Stats, StatsValue} from "~/server/utils/types/stats"; +import {H3Event} from "h3"; + +export type StatExport = { + labels: string[] + data: { + label: string + backgroundColor: string, + data: number[] + }[] +} + +export async function exportStats(event: H3Event, fn: (value: StatsValue) => number): Promise { + const storage = useStorage("statistics"); + let query = getQuery(event); + + let stats + + switch (query.mode) { + case "minor" : + stats = await storage.getItem("versionStatsMinor") + break + case "all" : + stats = await storage.getItem("versionStatsAll") + break + default: + stats = await storage.getItem("versionStatsMajor") + } + + if (!stats) { + // todo - error message + return { + labels: [], + data: [] + } + } + + return { + labels: stats.versions, + data: stats.data.map(value => { + return { + label: value.name, + backgroundColor: getColorForLauncher(value.name), + data: value.values.map((stat) => { + return fn(stat) + }) + } + }) + } +} \ No newline at end of file diff --git a/server/utils/statistics.ts b/server/utils/statistics.ts index 8fe58bb..1fa8268 100644 --- a/server/utils/statistics.ts +++ b/server/utils/statistics.ts @@ -1,3 +1,8 @@ +import {Stats} from "~/server/utils/types/stats"; +import {GameVersion, Version} from "~/server/utils/fetchData"; + +type StatsData = Map> + export async function updateStatistics() { const projectIds = await getProjectIds(0); console.log("project ids", projectIds.length); @@ -5,22 +10,22 @@ export async function updateStatistics() { const versionIds = (await getVersionIds(projectIds)) console.log("version ids", versionIds.length); - let data = new Map>() - let versions = await getGameVersions() + let data: StatsData = new Map() + await analyzeVersionsFromIds(versionIds, data); - await insertVersionsFromIds(versionIds, data); + let versions = await getGameVersions() - let allDownloads = downloadsFromData(versions, data); + let allDownloads = StatsFromData(versions, data); let {gameVersions: minorGameVersions, downloads: minorVersionDownloads} = onlyMinorVersions(versions, allDownloads); let majorVersionDownloads = onlyMajorVersions(minorGameVersions, minorVersionDownloads); const storage = useStorage("statistics"); - await storage.setItem("allVersionDownloads", allDownloads) - await storage.setItem("minorVersionDownloads", minorVersionDownloads) - await storage.setItem("majorVersionDownloads", majorVersionDownloads) + await storage.setItem("versionStatsAll", allDownloads) + await storage.setItem("versionStatsMinor", minorVersionDownloads) + await storage.setItem("versionStatsMajor", majorVersionDownloads) } -async function insertVersionsFromIds(versionIds: string[], data: Map>) { +async function analyzeVersionsFromIds(versionIds: string[], data: StatsData) { const BATCH_SIZE = 1000; let currentIndex = 0; @@ -32,13 +37,13 @@ async function insertVersionsFromIds(versionIds: string[], data: Map>) { +function analyzeVersions(versions: Version[], data: StatsData) { for (let version of versions) { // compensate for a version contributing to multiple loaders and versions @@ -49,7 +54,7 @@ function insertVersions(versions: Version[], data: Map(); + let downloads = new Map(); if (data.has(loader)) { downloads = data.get(loader)!; @@ -59,22 +64,23 @@ function insertVersions(versions: Version[], data: Map= loader.downloads.length) { + if (index + 1 >= loader.values.length) { continue } - loader.downloads[index + 1] += downloads[0] + loader.values[index + 1].count += stats[0].count + loader.values[index + 1].downloads += stats[0].downloads } versions.splice(index, 1) @@ -106,9 +113,9 @@ function onlyMinorVersions(gameVersions: GameVersion[], all: Downloads): { return {gameVersions: versions, downloads: downloads} } -function onlyMajorVersions(gameVersions: GameVersion[], all: Downloads): Downloads { +function onlyMajorVersions(gameVersions: GameVersion[], all: Stats): Stats { const versions = Array.from(gameVersions) - const downloads: Downloads = JSON.parse(JSON.stringify(all)) + const downloads: Stats = JSON.parse(JSON.stringify(all)) let index = versions.length - 1 @@ -124,13 +131,14 @@ function onlyMajorVersions(gameVersions: GameVersion[], all: Downloads): Downloa } for (let loader of downloads.data) { - let downloads = loader.downloads.splice(index, 1); + let stats = loader.values.splice(index, 1); if (index - 1 < 0) { continue } - loader.downloads[index - 1] += downloads[0] + loader.values[index - 1].count += stats[0].count + loader.values[index - 1].downloads += stats[0].downloads } versions.splice(index, 1) @@ -140,7 +148,7 @@ function onlyMajorVersions(gameVersions: GameVersion[], all: Downloads): Downloa return downloads } -function downloadsFromData(versions: GameVersion[], data: Map>): Downloads { +function StatsFromData(versions: GameVersion[], data: StatsData): Stats { const versionArray = versions.map(value => { return value.name }) @@ -155,17 +163,20 @@ function downloadsFromData(versions: GameVersion[], data: Map { - return Math.round(value) + return { + downloads: Math.round(value.downloads), + count: value.count + } }) loaderData.push({ name: loader[0], - downloads: downloads + values: downloads }) } diff --git a/server/utils/types/downloads.ts b/server/utils/types/downloads.ts deleted file mode 100644 index 24349a1..0000000 --- a/server/utils/types/downloads.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type Downloads = { - versions: string[] - data: { - name: string - downloads: number[] - }[] -} \ No newline at end of file diff --git a/server/utils/types/stats.ts b/server/utils/types/stats.ts new file mode 100644 index 0000000..e947af5 --- /dev/null +++ b/server/utils/types/stats.ts @@ -0,0 +1,9 @@ +export type Stats = { + versions: string[] + data: { + name: string + values: StatsValue[] + }[] +} + +export type StatsValue = { downloads: number, count: number } \ No newline at end of file -- 2.51.2