diff --git a/locales/de.yml b/locales/de.yml index 0120db5..26e66d3 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -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" diff --git a/locales/en.yml b/locales/en.yml index 1bd16f5..f5d519c 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -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" diff --git a/src/game.rs b/src/game.rs index b2f0186..2610afd 100644 --- a/src/game.rs +++ b/src/game.rs @@ -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)) } }); diff --git a/src/model/highscore.rs b/src/model/highscore.rs index 0a7629f..3ccf6aa 100644 --- a/src/model/highscore.rs +++ b/src/model/highscore.rs @@ -1,40 +1,102 @@ -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 { + 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 { match self { Backend::Sqlite(db) => { let rows = sqlx::query!( - "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 - ORDER BY amount DESC" + "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 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 = 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 } } }