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
objectives_with_points = []
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"):
points = obj["total_points"] or 0
objectives_with_points.append(
with_points[obj["objectiv_id"]].update(
{
"objectiv_id": obj["objectiv_id"],
"count": obj["count"],
"points_per_objectiv": obj["points_per_objectiv"] or 0,
"total_points": points,
"first_seen_at": obj["first_seen_at"],
"seed": obj["seed"],
@ -109,7 +119,7 @@ def get_results(request: HttpRequest):
data = {
"total_score": total_score,
"deaths_count": deaths_count,
"objectives": objectives_with_points,
"objectives": list(with_points.values()),
}
cache.set(f"api:noita:results:{request.user.id}", data, 300)

View File

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

View File

@ -14,8 +14,11 @@ import {
interface Objective {
objectiv_id: string;
first_seen_at: string;
seed: string;
display_string: string;
first_seen_at: string | null;
count: number;
max_count: number;
seed: string | null;
points_per_objectiv: number;
total_points: number;
}
@ -40,23 +43,21 @@ const columnHelper = createColumnHelper<Objective>();
const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]);
const formatDate = (dateString: string) => {
const formatDate = (dateString: string | null) => {
if (!dateString) {
return ""
}
const date = dayjs(dateString);
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 = [
columnHelper.accessor("objectiv_id", {
header: "Objective ID",
cell: (info) => info.getValue(),
}),
columnHelper.accessor("total_points", {
header: "Total Points",
header: "Your points",
cell: (info) => info.getValue() || 0,
}),
columnHelper.accessor("first_seen_at", {
@ -65,6 +66,12 @@ const columns = [
sortingFn: (rowA, rowB) => {
const dateA = dayjs(rowA.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;
},
}),
@ -104,9 +111,7 @@ const table = computed(() =>
const itemData = row.getValue(columnId);
const searchValue = value.toLowerCase();
if (columnId === "first_seen_at") {
const dateStr = itemData as string;
const formatted = dayjs(dateStr).format("MMM DD, YYYY HH:mm");
return formatted.toLowerCase().includes(searchValue);
return formatDate(itemData as string).includes(searchValue)
}
return String(itemData).toLowerCase().includes(searchValue);
},
@ -284,7 +289,7 @@ onMounted(() => {
<div class="min-h-screen bg-base-200">
<!-- Header -->
<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">
<i class="mdi mdi-arrow-left"></i>
Back
@ -297,14 +302,16 @@ onMounted(() => {
</div>
<!-- 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">
<!-- Left Column: User Ranking -->
<div class="lg:col-span-1">
<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">
<i class="mdi mdi-trophy text-5xl"></i>
<h2 class="text-3xl font-bold mt-3">Your Ranking</h2>
<h2 class="text-3xl font-bold">
<i class="mdi mdi-trophy text-3xl"></i>
Your Ranking
</h2>
</div>
<div class="card-body p-8">
<div class="text-center mb-8">
@ -455,7 +462,7 @@ onMounted(() => {
<div class="card-body">
<h2 class="card-title text-2xl mb-6">
<i class="mdi mdi-view-list text-purple-500 mr-2"></i>
Your Objectives
Objectives
</h2>
<div v-if="objectives.length === 0" class="text-center py-8">
@ -509,16 +516,21 @@ onMounted(() => {
cell.column.id === 'objectiv_id'
? 'font-medium'
: 'text-right',
cell.column.id === 'total_points' ? 'font-bold text-primary' : '',
]">
<template v-if="cell.column.id === 'objectiv_id'">
<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>
</a>
</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'">
<span :title="getDateTooltip(row.original.first_seen_at)">
<span :title="formatDate(row.original.first_seen_at)">
{{ formatDate(row.original.first_seen_at) }}
</span>
</template>

View File

@ -118,7 +118,7 @@ const goHome = () => {
<div class="min-h-screen bg-base-200">
<!-- Header -->
<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">
<i class="mdi mdi-arrow-left"></i>
Back
@ -140,7 +140,7 @@ const goHome = () => {
</div>
<!-- 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 -->
<div v-if="userInfo?.is_superuser" class="flex justify-center">
<div class="text-center">

View File

@ -173,9 +173,11 @@ onMounted(() => {
<!-- Left Column: Your Ranking -->
<div class="lg:col-span-1">
<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">
<i class="mdi mdi-trophy text-4xl"></i>
<h3 class="text-2xl font-bold mt-2">Your Ranking</h3>
<div class="bg-gradient-to-br from-purple-600 to-purple-400 p-6 text-white rounded-t-2xl">
<h3 class="text-3xl font-bold">
<i class="mdi mdi-trophy text-3xl"></i>
Your Ranking
</h3>
</div>
<div class="card-body">
<div class="text-center mb-6">
@ -315,17 +317,17 @@ onMounted(() => {
<div class="grid grid-cols-3 gap-4">
<div class="text-center">
<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>
</div>
<div class="text-center">
<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>
</div>
<div class="text-center">
<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>
</div>
</div>