only show top 10 participants + total amount

This commit is contained in:
2025-08-13 16:14:07 +02:00
parent 4ea9068850
commit 6d021e8d6b
4 changed files with 90 additions and 17 deletions

View File

@@ -29,6 +29,7 @@ location_linz: "Wo: überall in Linz"
game_title: "Wer findet die meisten Kameras?"
game_explanation_todo: "Willkommen zu unserem Überwachungsbewusstseinsspiel! Als Teil unserer Digital Shadows Ausstellung beim Ars Electronica Festival haben wir QR-Codes bei Überwachungskameras in ganz Linz platziert. Deine Mission: Entdecke die Kameras, scanne unsere Codes und finde heraus, wie allgegenwärtig öffentliche Überwachung wirklich ist. Wir sind aber nur Menschen wir haben nur einen kleinen Teil aller Kameras erfasst, die unsere Stadt beobachten. Wer beobachtet wen in unseren öffentlichen Räumen? Die Jagd beginnt jetzt! 🕵️"
save_button: "Speichern"
amount_participants: "In total there are %{amount} participants so far."
cameras_found: "Du hast %{found}/%{total} Kameras gefunden:"
highscore_title: "Bestenliste"
not_found_title: "ups"

View File

@@ -29,6 +29,7 @@ location_linz: "Where: all over Linz"
game_title: "Who finds the most cameras?"
game_explanation_todo: "Welcome to our public surveillance awareness game! As part of our Digital Shadows exhibition at Ars Electronica Festival, we've placed QR codes near surveillance cameras throughout Linz. Your mission: spot the cameras, scan our codes, and discover how pervasive public monitoring really is. We're only human though we've mapped just a small subset of all the cameras watching our city. Who's watching whom in our public spaces? The hunt begins now! 🕵️"
save_button: "Save"
amount_participants: "Aktuell gibt es insgesamt %{amount} Teilnehmer."
cameras_found: "You have found %{found}/%{total} cameras:"
highscore_title: "Highscore"
not_found_title: "uups"

View File

@@ -1,17 +1,17 @@
use crate::{
AppState, Backend, NameUpdateError,
language::language,
page::{MyMessage, Page},
AppState, Backend, NameUpdateError,
};
use axum::{
Form, Router,
extract::{Path, State},
http::HeaderMap,
response::{IntoResponse, Response},
routing::{get, post},
Form, Router,
};
use axum_extra::extract::{CookieJar, PrivateCookieJar};
use maud::{Markup, PreEscaped, html};
use maud::{html, Markup, PreEscaped};
use serde::Deserialize;
use std::sync::Arc;
use uuid::Uuid;
@@ -38,7 +38,8 @@ async fn retu(
let sightings = backend.sightings_for_client(&client).await;
let amount_total_cameras = backend.amount_total_cameras().await;
let highscore = backend.highscore().await;
let highscore = backend.highscore(&client).await;
let amount_participants = backend.amount_participants().await;
let mut page = Page::new(req.lang);
if let Some(message) = message {
@@ -85,6 +86,13 @@ async fn retu(
h2 { (t!("highscore_title")) }
ul.iterated {
@for rank in highscore {
@if rank.show_dots_above {
li.card {
span {
""
}
}
}
li.card {
span {
span.font-headline.rank.text-muted { (rank.rank) "." }
@@ -100,6 +108,7 @@ async fn retu(
}
}
}
(t!("amount_participants", amount = amount_participants))
}
});

View File

@@ -1,17 +1,32 @@
use crate::{Backend, model::client::Client};
use crate::{model::client::Client, Backend};
pub(crate) struct Rank {
pub(crate) rank: i64,
pub(crate) client: Client,
pub(crate) amount: i64,
pub(crate) show_dots_above: bool,
}
impl Backend {
pub(crate) async fn highscore(&self) -> Vec<Rank> {
pub(crate) async fn amount_participants(&self) -> i64 {
match self {
Backend::Sqlite(db) => {
let row = sqlx::query!("SELECT COUNT(*) as count FROM client")
.fetch_one(db)
.await
.unwrap();
row.count
}
}
}
pub(crate) async fn highscore(&self, client: &Client) -> Vec<Rank> {
match self {
Backend::Sqlite(db) => {
let rows = sqlx::query!(
"SELECT
"WITH ranked_clients AS (
SELECT
DENSE_RANK() OVER (ORDER BY COUNT(s.client_uuid) DESC) as rank,
c.name,
c.uuid,
@@ -19,22 +34,69 @@ impl Backend {
FROM client c
LEFT JOIN sightings s ON c.uuid = s.client_uuid
GROUP BY c.uuid, c.name
ORDER BY amount DESC"
)
SELECT rank, name, uuid, amount
FROM ranked_clients
WHERE rank <= (
SELECT rank
FROM ranked_clients
ORDER BY rank
LIMIT 1 OFFSET 9
)
ORDER BY rank, name"
)
.fetch_all(db)
.await
.unwrap_or_default();
rows.into_iter()
let mut ret: Vec<Rank> = rows
.into_iter()
.map(|row| Rank {
rank: row.rank.unwrap(),
client: Client {
uuid: row.uuid.unwrap(),
name: row.name,
},
amount: row.amount.unwrap(),
show_dots_above: false,
})
.collect();
let user_is_in_top = ret.iter().find(|x| &x.client == client).is_some();
if !user_is_in_top {
let row = sqlx::query!(
"WITH ranked_clients AS (
SELECT
DENSE_RANK() OVER (ORDER BY COUNT(s.client_uuid) DESC) as rank,
c.name,
c.uuid,
COUNT(s.client_uuid) as amount
FROM client c
LEFT JOIN sightings s ON c.uuid = s.client_uuid
GROUP BY c.uuid, c.name
)
SELECT rank, name, uuid, amount
FROM ranked_clients
WHERE uuid = ?
ORDER BY rank, name",
client.uuid
)
.fetch_one(db)
.await
.unwrap();
ret.push(Rank {
rank: row.rank,
client: Client {
uuid: row.uuid,
name: row.name,
},
amount: row.amount,
show_dots_above: true,
})
.collect()
}
ret
}
}
}