fix(noita): all objectives display

This commit is contained in:
Loïc Gremaud 2026-05-15 20:43:30 +02:00
parent 2ee8cd6be3
commit a800c7fff7
Signed by: Legrems
GPG Key ID: D4620E6DF3E0121D
5 changed files with 63 additions and 36 deletions

View File

@ -87,15 +87,25 @@ def get_results(request: HttpRequest):
) )
# Build response with all objectives and compute total score # Build response with all objectives and compute total score
objectives_with_points = []
total_score = 0 total_score = 0
with_points = {}
for obj in ObjectivPoint.objects.all():
with_points[obj.objectiv_id] = {
"objectiv_id": obj.objectiv_id,
"display_string": obj.display_string,
"count": 0,
"max_count": obj.max_count,
"points_per_objectiv": obj.point,
"total_points": 0,
"first_seen_at": None,
"seed": None,
}
for obj in user_objectives.order_by("-total_points"): for obj in user_objectives.order_by("-total_points"):
points = obj["total_points"] or 0 points = obj["total_points"] or 0
objectives_with_points.append( with_points[obj["objectiv_id"]].update(
{ {
"objectiv_id": obj["objectiv_id"],
"count": obj["count"], "count": obj["count"],
"points_per_objectiv": obj["points_per_objectiv"] or 0,
"total_points": points, "total_points": points,
"first_seen_at": obj["first_seen_at"], "first_seen_at": obj["first_seen_at"],
"seed": obj["seed"], "seed": obj["seed"],
@ -109,7 +119,7 @@ def get_results(request: HttpRequest):
data = { data = {
"total_score": total_score, "total_score": total_score,
"deaths_count": deaths_count, "deaths_count": deaths_count,
"objectives": objectives_with_points, "objectives": list(with_points.values()),
} }
cache.set(f"api:noita:results:{request.user.id}", data, 300) cache.set(f"api:noita:results:{request.user.id}", data, 300)

View File

@ -15,10 +15,13 @@ class NoitaSubmissionOut(Schema):
class ObjectivResultOut(Schema): class ObjectivResultOut(Schema):
objectiv_id: str objectiv_id: str
first_seen_at: datetime display_string: str
seed: str first_seen_at: datetime | None
count: int
max_count: int
seed: str | None
points_per_objectiv: int points_per_objectiv: int
total_points: int total_points: int | None
class ResultsOut(Schema): class ResultsOut(Schema):

View File

@ -14,8 +14,11 @@ import {
interface Objective { interface Objective {
objectiv_id: string; objectiv_id: string;
first_seen_at: string; display_string: string;
seed: string; first_seen_at: string | null;
count: number;
max_count: number;
seed: string | null;
points_per_objectiv: number; points_per_objectiv: number;
total_points: number; total_points: number;
} }
@ -40,23 +43,21 @@ const columnHelper = createColumnHelper<Objective>();
const sorting = ref<SortingState>([]); const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]); const columnFilters = ref<ColumnFiltersState>([]);
const formatDate = (dateString: string) => { const formatDate = (dateString: string | null) => {
if (!dateString) {
return ""
}
const date = dayjs(dateString); const date = dayjs(dateString);
return date.format("MMM DD, YYYY HH:mm"); return date.format("MMM DD, YYYY HH:mm");
}; };
const getDateTooltip = (dateString: string) => {
const date = dayjs(dateString);
return date.format("dddd, MMMM D, YYYY [at] h:mm A");
};
const columns = [ const columns = [
columnHelper.accessor("objectiv_id", { columnHelper.accessor("objectiv_id", {
header: "Objective ID", header: "Objective ID",
cell: (info) => info.getValue(), cell: (info) => info.getValue(),
}), }),
columnHelper.accessor("total_points", { columnHelper.accessor("total_points", {
header: "Total Points", header: "Your points",
cell: (info) => info.getValue() || 0, cell: (info) => info.getValue() || 0,
}), }),
columnHelper.accessor("first_seen_at", { columnHelper.accessor("first_seen_at", {
@ -65,6 +66,12 @@ const columns = [
sortingFn: (rowA, rowB) => { sortingFn: (rowA, rowB) => {
const dateA = dayjs(rowA.original.first_seen_at); const dateA = dayjs(rowA.original.first_seen_at);
const dateB = dayjs(rowB.original.first_seen_at); const dateB = dayjs(rowB.original.first_seen_at);
if (!rowA.original.first_seen_at) {
return rowB.original.first_seen_at ? 1 : 0
}
if (!rowB.original.first_seen_at) {
return rowA.original.first_seen_at ? 0 : 1
}
return dateA.isBefore(dateB) ? -1 : dateA.isAfter(dateB) ? 1 : 0; return dateA.isBefore(dateB) ? -1 : dateA.isAfter(dateB) ? 1 : 0;
}, },
}), }),
@ -104,9 +111,7 @@ const table = computed(() =>
const itemData = row.getValue(columnId); const itemData = row.getValue(columnId);
const searchValue = value.toLowerCase(); const searchValue = value.toLowerCase();
if (columnId === "first_seen_at") { if (columnId === "first_seen_at") {
const dateStr = itemData as string; return formatDate(itemData as string).includes(searchValue)
const formatted = dayjs(dateStr).format("MMM DD, YYYY HH:mm");
return formatted.toLowerCase().includes(searchValue);
} }
return String(itemData).toLowerCase().includes(searchValue); return String(itemData).toLowerCase().includes(searchValue);
}, },
@ -284,7 +289,7 @@ onMounted(() => {
<div class="min-h-screen bg-base-200"> <div class="min-h-screen bg-base-200">
<!-- Header --> <!-- Header -->
<div class="navbar bg-base-100 shadow-lg"> <div class="navbar bg-base-100 shadow-lg">
<div class="container mx-auto w-full flex items-center gap-4"> <div class="container min-w-3/4 mx-auto w-full flex items-center gap-4">
<button @click="goHome" class="btn btn-primary btn-sm"> <button @click="goHome" class="btn btn-primary btn-sm">
<i class="mdi mdi-arrow-left"></i> <i class="mdi mdi-arrow-left"></i>
Back Back
@ -297,14 +302,16 @@ onMounted(() => {
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div class="container mx-auto px-4 py-8"> <div class="container min-w-3/4 mx-auto px-4 py-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Left Column: User Ranking --> <!-- Left Column: User Ranking -->
<div class="lg:col-span-1"> <div class="lg:col-span-1">
<div class="card bg-base-100 shadow-lg sticky top-8"> <div class="card bg-base-100 shadow-lg sticky top-8">
<div class="bg-gradient-to-br from-purple-600 to-purple-400 p-8 text-white rounded-t-2xl"> <div class="bg-gradient-to-br from-purple-600 to-purple-400 p-8 text-white rounded-t-2xl">
<i class="mdi mdi-trophy text-5xl"></i> <h2 class="text-3xl font-bold">
<h2 class="text-3xl font-bold mt-3">Your Ranking</h2> <i class="mdi mdi-trophy text-3xl"></i>
Your Ranking
</h2>
</div> </div>
<div class="card-body p-8"> <div class="card-body p-8">
<div class="text-center mb-8"> <div class="text-center mb-8">
@ -455,7 +462,7 @@ onMounted(() => {
<div class="card-body"> <div class="card-body">
<h2 class="card-title text-2xl mb-6"> <h2 class="card-title text-2xl mb-6">
<i class="mdi mdi-view-list text-purple-500 mr-2"></i> <i class="mdi mdi-view-list text-purple-500 mr-2"></i>
Your Objectives Objectives
</h2> </h2>
<div v-if="objectives.length === 0" class="text-center py-8"> <div v-if="objectives.length === 0" class="text-center py-8">
@ -509,16 +516,21 @@ onMounted(() => {
cell.column.id === 'objectiv_id' cell.column.id === 'objectiv_id'
? 'font-medium' ? 'font-medium'
: 'text-right', : 'text-right',
cell.column.id === 'total_points' ? 'font-bold text-primary' : '',
]"> ]">
<template v-if="cell.column.id === 'objectiv_id'"> <template v-if="cell.column.id === 'objectiv_id'">
<a :href="`https://noita.wiki.gg/wiki/${row.original.objectiv_id}`" target="_blank"> <a :href="`https://noita.wiki.gg/wiki/${row.original.objectiv_id}`" target="_blank">
{{ row.original.objectiv_id }} {{ row.original.display_string }}
<i class="mdi mdi-open-in-new"></i> <i class="mdi mdi-open-in-new"></i>
</a> </a>
</template> </template>
<template v-else-if="cell.column.id === 'total_points'">
<span :class="row.original.count >= row.original.max_count ? 'text-primary' : 'text-error'">
{{ row.original.total_points }} / {{ row.original.points_per_objectiv *
row.original.max_count }}
</span>
</template>
<template v-else-if="cell.column.id === 'first_seen_at'"> <template v-else-if="cell.column.id === 'first_seen_at'">
<span :title="getDateTooltip(row.original.first_seen_at)"> <span :title="formatDate(row.original.first_seen_at)">
{{ formatDate(row.original.first_seen_at) }} {{ formatDate(row.original.first_seen_at) }}
</span> </span>
</template> </template>

View File

@ -118,7 +118,7 @@ const goHome = () => {
<div class="min-h-screen bg-base-200"> <div class="min-h-screen bg-base-200">
<!-- Header --> <!-- Header -->
<div class="navbar bg-base-100 shadow-lg"> <div class="navbar bg-base-100 shadow-lg">
<div class="container mx-auto w-full flex items-center gap-4"> <div class="container min-w-3/4 mx-auto w-full flex items-center gap-4">
<button @click="goHome" class="btn btn-primary btn-sm"> <button @click="goHome" class="btn btn-primary btn-sm">
<i class="mdi mdi-arrow-left"></i> <i class="mdi mdi-arrow-left"></i>
Back Back
@ -140,7 +140,7 @@ const goHome = () => {
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div class="container mx-auto px-4 py-8"> <div class="container min-w-3/4 mx-auto px-4 py-8">
<!-- Loading State --> <!-- Loading State -->
<div v-if="userInfo?.is_superuser" class="flex justify-center"> <div v-if="userInfo?.is_superuser" class="flex justify-center">
<div class="text-center"> <div class="text-center">

View File

@ -173,9 +173,11 @@ onMounted(() => {
<!-- Left Column: Your Ranking --> <!-- Left Column: Your Ranking -->
<div class="lg:col-span-1"> <div class="lg:col-span-1">
<div class="card bg-base-100 shadow-lg sticky top-8"> <div class="card bg-base-100 shadow-lg sticky top-8">
<div class="bg-gradient-to-br from-blue-600 to-blue-400 p-6 text-white rounded-t-2xl"> <div class="bg-gradient-to-br from-purple-600 to-purple-400 p-6 text-white rounded-t-2xl">
<i class="mdi mdi-trophy text-4xl"></i> <h3 class="text-3xl font-bold">
<h3 class="text-2xl font-bold mt-2">Your Ranking</h3> <i class="mdi mdi-trophy text-3xl"></i>
Your Ranking
</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="text-center mb-6"> <div class="text-center mb-6">
@ -315,17 +317,17 @@ onMounted(() => {
<div class="grid grid-cols-3 gap-4"> <div class="grid grid-cols-3 gap-4">
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cost <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cost
}}</span> }}</span>
<p class="text-xs text-base-content/70">Cost</p> <p class="text-xs text-base-content/70">Cost</p>
</div> </div>
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cycles <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.cycles
}}</span> }}</span>
<p class="text-xs text-base-content/70">Cycles</p> <p class="text-xs text-base-content/70">Cycles</p>
</div> </div>
<div class="text-center"> <div class="text-center">
<span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.area <span class="text-2xl font-bold text-primary"><small>x</small>{{ puzzle.points_factor.area
}}</span> }}</span>
<p class="text-xs text-base-content/70">Area</p> <p class="text-xs text-base-content/70">Area</p>
</div> </div>
</div> </div>