refactor: update StatsReturn structure and related components; remove unused code and enhance platform stats display

This commit is contained in:
zurdi
2025-05-20 16:09:03 +00:00
parent a53e809687
commit c2faccabc3
13 changed files with 193 additions and 230 deletions

View File

@@ -2,10 +2,9 @@ from typing import TypedDict
class StatsReturn(TypedDict):
PLATFORMS_COUNT: int
PLATFORMS: list
PLATFORMS: int
ROMS: int
SAVES: int
STATES: int
SCREENSHOTS: int
TOTAL_FILESIZE: int
TOTAL_FILESIZE_BYTES: int

View File

@@ -17,11 +17,10 @@ def stats() -> StatsReturn:
"""
return {
"PLATFORMS_COUNT": db_stats_handler.get_platforms_count(),
"PLATFORMS": db_stats_handler.get_platforms_filesize(),
"PLATFORMS": db_stats_handler.get_platforms_count(),
"ROMS": db_stats_handler.get_roms_count(),
"SAVES": db_stats_handler.get_saves_count(),
"STATES": db_stats_handler.get_states_count(),
"SCREENSHOTS": db_stats_handler.get_screenshots_count(),
"TOTAL_FILESIZE": db_stats_handler.get_total_filesize(),
"TOTAL_FILESIZE_BYTES": db_stats_handler.get_total_filesize(),
}

View File

@@ -57,24 +57,3 @@ class DBStatsHandler(DBBaseHandler):
)
or 0
)
@begin_session
def get_platforms_filesize(self, session: Session = None) -> list[dict]:
"""
Returns a list of dicts: each with 'id', 'name', and 'filesize' for each platform.
"""
platform_rows = (
session.execute(
select(Platform.id, Platform.name)
.join(Rom, Rom.platform_id == Platform.id)
.group_by(Platform.id, Platform.name)
)
).all()
return [
{
"id": platform_id,
"name": platform_name,
"filesize": self.get_platform_filesize(platform_id, session=session),
}
for platform_id, platform_name in platform_rows
]

View File

@@ -3,12 +3,11 @@
/* tslint:disable */
/* eslint-disable */
export type StatsReturn = {
PLATFORMS_COUNT: number;
PLATFORMS: Array<any>;
PLATFORMS: number;
ROMS: number;
SAVES: number;
STATES: number;
SCREENSHOTS: number;
TOTAL_FILESIZE: number;
TOTAL_FILESIZE_BYTES: number;
};

View File

@@ -1,86 +0,0 @@
<script setup lang="ts">
import { formatBytes } from "@/utils";
import { useI18n } from "vue-i18n";
// Props
defineProps<{
stats: {
PLATFORMS_COUNT: number;
ROMS: number;
SAVES: number;
STATES: number;
SCREENSHOTS: number;
TOTAL_FILESIZE: number;
};
}>();
const { t } = useI18n();
</script>
<template>
<v-card>
<v-card-text class="pa-1">
<v-row no-gutters class="flex-nowrap overflow-x-auto text-center">
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-controller"
variant="text"
label
>
{{ t("common.platforms-n", stats.PLATFORMS_COUNT) }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-disc"
variant="text"
label
>
{{ t("common.games-n", stats.ROMS) }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-content-save"
variant="text"
label
>
{{ t("common.saves-n", stats.SAVES) }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-file"
variant="text"
label
>
{{ t("common.states-n", stats.STATES) }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-image-area"
variant="text"
label
>
{{ t("common.screenshots-n", stats.SCREENSHOTS) }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="text-overline"
prepend-icon="mdi-harddisk"
variant="text"
label
>
{{ t("common.size-on-disk") }}:
{{ formatBytes(stats.TOTAL_FILESIZE, 1) }}
</v-chip>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>

View File

@@ -1,46 +0,0 @@
<script setup lang="ts">
import { formatBytes } from "@/utils";
// Props
const props = defineProps<{
platforms: Array<{ id: number; name: string; filesize: number | string }>;
total: number;
}>();
// Functions
function getPercentage(filesize: number | string, total: number): number {
const size = typeof filesize === "string" ? parseInt(filesize, 10) : filesize;
if (!total || isNaN(size)) return 0;
return (size / total) * 100;
}
function idToHexColor(id: number): string {
const knuthHash = 2654435761;
const hex = ((id * knuthHash) >>> 0).toString(16).padStart(6, "0");
return `#${hex.slice(0, 6)}`;
}
</script>
<template>
<v-card>
<v-card-text>
<div v-for="platform in props.platforms" :key="platform.id" class="mb-4">
<div class="d-flex justify-space-between align-center mb-1">
<span class="ml-2">
<strong>{{ platform.name }}</strong>
</span>
<span class="mr-2">
{{ formatBytes(Number(platform.filesize)) }}
({{ getPercentage(platform.filesize, props.total).toFixed(1) }}%)
</span>
</div>
<v-progress-linear
:model-value="getPercentage(platform.filesize, props.total)"
rounded
:color="idToHexColor(platform.id)"
height="15"
/>
</div>
</v-card-text>
</v-card>
</template>

View File

@@ -0,0 +1,72 @@
<script setup lang="ts">
import { formatBytes } from "@/utils";
import PlatformListItem from "@/components/common/Platform/ListItem.vue";
import storePlatforms from "@/stores/platforms";
import { storeToRefs } from "pinia";
// Props
const props = defineProps<{
total_filesize: number;
}>();
const platformsStore = storePlatforms();
const { filteredPlatforms } = storeToRefs(platformsStore);
// Functions
function getPercentage(filesize: number | string, total: number): number {
const size = typeof filesize === "string" ? parseInt(filesize, 10) : filesize;
if (!total || isNaN(size)) return 0;
return (size / total) * 100;
}
function idToHexColor(id: number): string {
const knuthHash = 2654435761;
const hex = ((id * knuthHash) >>> 0).toString(16).padStart(6, "0");
return `#${hex.slice(0, 6)}`;
}
</script>
<template>
<v-card>
<v-card-text>
<div
v-for="(platform, idx) in filteredPlatforms"
:class="{ 'mb-8': idx !== filteredPlatforms.length - 1 }"
>
<v-row no-gutters class="d-flex justify-space-between align-center">
<v-col cols="6">
<platform-list-item
:platform="platform"
:key="platform.slug"
:showRomCount="false"
/>
</v-col>
<v-col cols="6" class="text-right">
<v-list-item>
<v-list-item-title class="text-body-2">
{{ formatBytes(Number(platform.fs_size_bytes)) }}
({{
getPercentage(
platform.fs_size_bytes,
props.total_filesize,
).toFixed(1)
}}%)
</v-list-item-title>
<v-list-item-subtitle class="text-right mt-1">
{{ platform.rom_count }} roms
</v-list-item-subtitle>
</v-list-item>
</v-col>
</v-row>
<v-progress-linear
:model-value="
getPercentage(platform.fs_size_bytes, props.total_filesize)
"
rounded
:color="idToHexColor(platform.id)"
height="10"
/>
</div>
</v-card-text>
</v-card>
</template>

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
import { formatBytes } from "@/utils";
import { useI18n } from "vue-i18n";
defineProps<{
stats: {
PLATFORMS: number;
ROMS: number;
SAVES: number;
STATES: number;
SCREENSHOTS: number;
TOTAL_FILESIZE_BYTES: number;
};
}>();
const { t } = useI18n();
</script>
<template>
<v-card class="pa-4">
<v-card-title class="text-h6 text-truncate mb-2">
<v-icon class="mr-2">mdi-server</v-icon>
{{ t("rom.summary") }}
</v-card-title>
<v-divider class="mb-3"></v-divider>
<v-row dense>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-controller</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ t("common.platforms-n", stats.PLATFORMS) }}
</div>
</v-sheet>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-disc</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ t("common.games-n", stats.ROMS) }}
</div>
</v-sheet>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-content-save</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ t("common.saves-n", stats.SAVES) }}
</div>
</v-sheet>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-file</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ t("common.states-n", stats.STATES) }}
</div>
</v-sheet>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-image-area</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ t("common.screenshots-n", stats.SCREENSHOTS) }}
</div>
<div class="text-caption"></div>
</v-sheet>
</v-col>
<v-col cols="6" md="4" lg="2">
<v-sheet class="pa-3 text-center" color="toplayer" rounded>
<v-icon size="32">mdi-harddisk</v-icon>
<div class="text-h6 text-truncate font-weight-bold">
{{ formatBytes(stats.TOTAL_FILESIZE_BYTES, 1) }}
{{ t("common.size-on-disk") }}
</div>
</v-sheet>
</v-col>
</v-row>
</v-card>
</template>

View File

@@ -104,6 +104,7 @@ function clear() {
v-for="platform in platforms"
:key="platform.slug"
:platform="platform"
withLink
/>
</v-list>
</v-expansion-panel-text>
@@ -116,6 +117,7 @@ function clear() {
v-for="platform in filteredPlatforms"
:key="platform.slug"
:platform="platform"
withLink
/>
</v-list>
</template>

View File

@@ -4,14 +4,28 @@ import { ROUTES } from "@/plugins/router";
import type { Platform } from "@/stores/platforms";
// Props
withDefaults(defineProps<{ platform: Platform; rail?: boolean }>(), {
rail: false,
});
withDefaults(
defineProps<{
platform: Platform;
withLink?: boolean;
showRomCount?: boolean;
}>(),
{
withLink: false,
showRomCount: true,
},
);
</script>
<template>
<v-list-item
:to="{ name: ROUTES.PLATFORM, params: { platform: platform.id } }"
v-bind="{
...(withLink
? {
to: { name: ROUTES.PLATFORM, params: { platform: platform.id } },
}
: {}),
}"
:value="platform.slug"
rounded
density="compact"
@@ -52,7 +66,7 @@ withDefaults(defineProps<{ platform: Platform; rail?: boolean }>(), {
<span class="text-caption text-grey">{{ platform.fs_slug }}</span>
</v-col>
</v-row>
<template #append>
<template v-if="showRomCount" #append>
<v-chip class="ml-2" size="x-small" label>
{{ platform.rom_count }}
</v-chip>

View File

@@ -1,11 +0,0 @@
<script setup lang="ts">
import RFooter from "@/components/Settings/Footer.vue";
</script>
<template>
<!-- # Bottom padding needed to avoid footer overlap -->
<div class="mb-16">
<router-view />
</div>
<r-footer />
</template>

View File

@@ -1,4 +1,3 @@
// Composables
import {
createRouter,
createWebHistory,
@@ -144,58 +143,28 @@ const routes = [
},
{
path: "user/:user",
component: () => import("@/layouts/Settings.vue"),
children: [
{
path: "",
name: ROUTES.USER_PROFILE,
component: () => import("@/views/Settings/UserProfile.vue"),
},
],
name: ROUTES.USER_PROFILE,
component: () => import("@/views/Settings/UserProfile.vue"),
},
{
path: "user-interface",
component: () => import("@/layouts/Settings.vue"),
children: [
{
path: "",
name: ROUTES.USER_INTERFACE,
component: () => import("@/views/Settings/UserInterface.vue"),
},
],
name: ROUTES.USER_INTERFACE,
component: () => import("@/views/Settings/UserInterface.vue"),
},
{
path: "library-management",
component: () => import("@/layouts/Settings.vue"),
children: [
{
path: "",
name: ROUTES.LIBRARY_MANAGEMENT,
component: () => import("@/views/Settings/LibraryManagement.vue"),
},
],
name: ROUTES.LIBRARY_MANAGEMENT,
component: () => import("@/views/Settings/LibraryManagement.vue"),
},
{
path: "administration",
component: () => import("@/layouts/Settings.vue"),
children: [
{
path: "",
name: ROUTES.ADMINISTRATION,
component: () => import("@/views/Settings/Administration.vue"),
},
],
name: ROUTES.ADMINISTRATION,
component: () => import("@/views/Settings/Administration.vue"),
},
{
path: "server-stats",
component: () => import("@/layouts/Settings.vue"),
children: [
{
path: "",
name: ROUTES.SERVER_STATS,
component: () => import("@/views/Settings/ServerStats.vue"),
},
],
name: ROUTES.SERVER_STATS,
component: () => import("@/views/Settings/ServerStats.vue"),
},
{
path: ":pathMatch(.*)*",

View File

@@ -1,18 +1,17 @@
<script setup lang="ts">
import Stats from "@/components/Home/Stats.vue";
import PlatformsSize from "@/components/Settings/ServerStats/PlatformsSize.vue";
import SummaryStats from "@/components/Settings/ServerStats/SummaryStats.vue";
import PlatformsStats from "@/components/Settings/ServerStats/PlatformsStats.vue";
import api from "@/services/api/index";
import { onBeforeMount, ref } from "vue";
// Props
const stats = ref({
PLATFORMS_COUNT: 0,
PLATFORMS: [],
PLATFORMS: 0,
ROMS: 0,
SAVES: 0,
STATES: 0,
SCREENSHOTS: 0,
TOTAL_FILESIZE: 0,
TOTAL_FILESIZE_BYTES: 0,
});
// Functions
@@ -23,10 +22,6 @@ onBeforeMount(() => {
});
</script>
<template>
<stats class="ma-2" :stats="stats" />
<platforms-size
class="ma-2"
:platforms="stats.PLATFORMS"
:total="stats.TOTAL_FILESIZE"
/>
<summary-stats class="ma-2" :stats="stats" />
<platforms-stats class="ma-2" :total_filesize="stats.TOTAL_FILESIZE_BYTES" />
</template>