feat: include wattHours, tdpMax, and batteryLife in hardware and game stats APIs
This commit is contained in:
@@ -31,6 +31,8 @@ export default async function DevicesPage() {
|
|||||||
deviceType: hardware.deviceType,
|
deviceType: hardware.deviceType,
|
||||||
image: hardware.image,
|
image: hardware.image,
|
||||||
sortOrder: hardware.sortOrder,
|
sortOrder: hardware.sortOrder,
|
||||||
|
wattHours: hardware.wattHours,
|
||||||
|
tdpMax: hardware.tdpMax,
|
||||||
})
|
})
|
||||||
.from(hardware)
|
.from(hardware)
|
||||||
.orderBy(hardware.sortOrder)
|
.orderBy(hardware.sortOrder)
|
||||||
@@ -102,6 +104,8 @@ export default async function DevicesPage() {
|
|||||||
gameCount: stats?.gameCount ?? 0,
|
gameCount: stats?.gameCount ?? 0,
|
||||||
verifiedCount: stats?.verifiedCount ?? 0,
|
verifiedCount: stats?.verifiedCount ?? 0,
|
||||||
bestGame: bestGame ?? null,
|
bestGame: bestGame ?? null,
|
||||||
|
wattHours: device.wattHours ? Number(device.wattHours) : null,
|
||||||
|
tdpMax: device.tdpMax ? Number(device.tdpMax) : null,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+53
-2
@@ -46,6 +46,7 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
|||||||
userNotes: performanceEntries.userNotes,
|
userNotes: performanceEntries.userNotes,
|
||||||
createdAt: performanceEntries.createdAt,
|
createdAt: performanceEntries.createdAt,
|
||||||
versionId: performanceEntries.versionId,
|
versionId: performanceEntries.versionId,
|
||||||
|
tdpWatts: performanceEntries.tdpWatts,
|
||||||
})
|
})
|
||||||
.from(performanceEntries)
|
.from(performanceEntries)
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
@@ -76,6 +77,7 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
|||||||
fpsRange: [],
|
fpsRange: [],
|
||||||
deviceBreakdown: [],
|
deviceBreakdown: [],
|
||||||
filterOptions: { protonVersions: [], osVersions: [] },
|
filterOptions: { protonVersions: [], osVersions: [] },
|
||||||
|
batteryLife: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,11 +247,17 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
|||||||
|
|
||||||
// ── 8. Device breakdown ───────────────────────────────────────
|
// ── 8. Device breakdown ───────────────────────────────────────
|
||||||
const deviceBreakdown = Array.from(boxplotMap.entries()).map(
|
const deviceBreakdown = Array.from(boxplotMap.entries()).map(
|
||||||
([slug, { hardwareName, values }]) => ({
|
([slug, { hardwareName, values }]) => {
|
||||||
|
const dev = deviceWattHoursMap.get(slug)
|
||||||
|
return {
|
||||||
hardwareSlug: slug,
|
hardwareSlug: slug,
|
||||||
hardwareName,
|
hardwareName,
|
||||||
count: values.length,
|
count: values.length,
|
||||||
}),
|
wattHours: dev?.wattHours ? Number(dev.wattHours) : null,
|
||||||
|
tdpMax: dev?.tdpMax ? Number(dev.tdpMax) : null,
|
||||||
|
deviceType: dev?.deviceType ?? null,
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// ── 9. Performance tier breakdown per device ────────────────
|
// ── 9. Performance tier breakdown per device ────────────────
|
||||||
@@ -279,6 +287,48 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
|||||||
stabilityRatio: e.fpsAvg > 0 ? Math.min(1, e.fpsOnePercentLow! / e.fpsAvg) : 0,
|
stabilityRatio: e.fpsAvg > 0 ? Math.min(1, e.fpsOnePercentLow! / e.fpsAvg) : 0,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// ── 12. Battery life estimates ─────────────────────────────
|
||||||
|
// Fetch hardware wattHours for the relevant devices
|
||||||
|
const deviceSlugs = [...new Set(entries.map((e) => e.hardwareSlug))]
|
||||||
|
const deviceData = await db
|
||||||
|
.select({
|
||||||
|
slug: hardware.slug,
|
||||||
|
wattHours: hardware.wattHours,
|
||||||
|
tdpMax: hardware.tdpMax,
|
||||||
|
deviceType: hardware.deviceType,
|
||||||
|
})
|
||||||
|
.from(hardware)
|
||||||
|
.where(sql`${hardware.slug} = ANY(${deviceSlugs})`)
|
||||||
|
|
||||||
|
const deviceWattHoursMap = new Map(
|
||||||
|
deviceData.map((d) => [d.slug, d]),
|
||||||
|
)
|
||||||
|
|
||||||
|
const batteryLife = entries
|
||||||
|
.filter((e) => e.tdpWatts != null && e.tdpWatts > 0)
|
||||||
|
.map((e) => {
|
||||||
|
const device = deviceWattHoursMap.get(e.hardwareSlug)
|
||||||
|
const wh = device?.wattHours ? Number(device.wattHours) : null
|
||||||
|
const tdpMax = device?.tdpMax ? Number(device.tdpMax) : null
|
||||||
|
if (!wh) return null
|
||||||
|
const estimatedBatteryHours = wh / e.tdpWatts!
|
||||||
|
const estimatedBatteryMin = estimatedBatteryHours * 60
|
||||||
|
const estimatedAtMaxTdpMin = tdpMax ? (wh / tdpMax) * 60 : null
|
||||||
|
return {
|
||||||
|
id: e.id,
|
||||||
|
hardwareSlug: e.hardwareSlug,
|
||||||
|
tdpWatts: Number(e.tdpWatts),
|
||||||
|
estimatedBatteryMin: Math.round(estimatedBatteryMin),
|
||||||
|
estimatedBatteryHours: Math.round(estimatedBatteryHours * 10) / 10,
|
||||||
|
wattHours: wh,
|
||||||
|
tdpMax,
|
||||||
|
estimatedAtMaxTdpMin: estimatedAtMaxTdpMin
|
||||||
|
? Math.round(estimatedAtMaxTdpMin)
|
||||||
|
: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
// ── 11. Filter options ────────────────────────────────────────
|
// ── 11. Filter options ────────────────────────────────────────
|
||||||
const protonVersions = [
|
const protonVersions = [
|
||||||
...new Set(entries.map((e) => e.protonVersion).filter(Boolean)),
|
...new Set(entries.map((e) => e.protonVersion).filter(Boolean)),
|
||||||
@@ -306,6 +356,7 @@ export const gameStatsRoutes = new Elysia({ prefix: "/games" }).get(
|
|||||||
deviceBreakdown,
|
deviceBreakdown,
|
||||||
performanceTiers,
|
performanceTiers,
|
||||||
stabilityScatter,
|
stabilityScatter,
|
||||||
|
batteryLife,
|
||||||
filterOptions: { protonVersions, osVersions },
|
filterOptions: { protonVersions, osVersions },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
|||||||
name: hardware.name,
|
name: hardware.name,
|
||||||
deviceType: hardware.deviceType,
|
deviceType: hardware.deviceType,
|
||||||
sortOrder: hardware.sortOrder,
|
sortOrder: hardware.sortOrder,
|
||||||
|
wattHours: hardware.wattHours,
|
||||||
|
tdpMax: hardware.tdpMax,
|
||||||
})
|
})
|
||||||
.from(hardware)
|
.from(hardware)
|
||||||
.orderBy(hardware.sortOrder)
|
.orderBy(hardware.sortOrder)
|
||||||
@@ -79,6 +81,8 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
|||||||
name: device.name,
|
name: device.name,
|
||||||
deviceType: device.deviceType,
|
deviceType: device.deviceType,
|
||||||
sortOrder: device.sortOrder,
|
sortOrder: device.sortOrder,
|
||||||
|
wattHours: device.wattHours ? Number(device.wattHours) : null,
|
||||||
|
tdpMax: device.tdpMax ? Number(device.tdpMax) : null,
|
||||||
totalBenchmarks: stats?.totalBenchmarks ?? 0,
|
totalBenchmarks: stats?.totalBenchmarks ?? 0,
|
||||||
avgFps: stats?.avgFps ? Number(stats.avgFps) : null,
|
avgFps: stats?.avgFps ? Number(stats.avgFps) : null,
|
||||||
gameCount: stats?.gameCount ?? 0,
|
gameCount: stats?.gameCount ?? 0,
|
||||||
@@ -100,6 +104,8 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
|||||||
slug: hardware.slug,
|
slug: hardware.slug,
|
||||||
name: hardware.name,
|
name: hardware.name,
|
||||||
deviceType: hardware.deviceType,
|
deviceType: hardware.deviceType,
|
||||||
|
wattHours: hardware.wattHours,
|
||||||
|
tdpMax: hardware.tdpMax,
|
||||||
})
|
})
|
||||||
.from(hardware)
|
.from(hardware)
|
||||||
.where(eq(hardware.slug, slug))
|
.where(eq(hardware.slug, slug))
|
||||||
@@ -144,6 +150,8 @@ export const hardwareStatsRoutes = new Elysia({ prefix: "/hardware" })
|
|||||||
if (entries.length === 0) {
|
if (entries.length === 0) {
|
||||||
return {
|
return {
|
||||||
...device,
|
...device,
|
||||||
|
wattHours: null,
|
||||||
|
tdpMax: null,
|
||||||
totalBenchmarks: 0,
|
totalBenchmarks: 0,
|
||||||
avgFps: null,
|
avgFps: null,
|
||||||
verifiedCount: 0,
|
verifiedCount: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user