From d3465b4e107a9a4f379ead0aa62b741249e99b56 Mon Sep 17 00:00:00 2001 From: Dietrich Date: Tue, 9 Feb 2021 21:30:11 +0100 Subject: [PATCH] Adding click statistics closes #2 --- .../2021-02-09-175834_add_statistics/down.sql | 3 ++ .../2021-02-09-175834_add_statistics/up.sql | 13 ++++++ src/models.rs | 30 +++++++++++++- src/schema.rs | 11 ++++- src/views.rs | 40 ++++++++++++++++--- templates/index.html | 7 ++++ 6 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 migrations/2021-02-09-175834_add_statistics/down.sql create mode 100644 migrations/2021-02-09-175834_add_statistics/up.sql diff --git a/migrations/2021-02-09-175834_add_statistics/down.sql b/migrations/2021-02-09-175834_add_statistics/down.sql new file mode 100644 index 0000000..6a7e706 --- /dev/null +++ b/migrations/2021-02-09-175834_add_statistics/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +DROP TABLE clicks; \ No newline at end of file diff --git a/migrations/2021-02-09-175834_add_statistics/up.sql b/migrations/2021-02-09-175834_add_statistics/up.sql new file mode 100644 index 0000000..056845d --- /dev/null +++ b/migrations/2021-02-09-175834_add_statistics/up.sql @@ -0,0 +1,13 @@ +-- Your SQL goes here + +CREATE TABLE clicks +( + id INTEGER PRIMARY KEY NOT NULL, + link INT NOT NULL, + created_at TIMESTAMP NOT NULL, + + FOREIGN KEY + (link) + REFERENCES links + (id) +); \ No newline at end of file diff --git a/src/models.rs b/src/models.rs index dc09ee7..810a767 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,6 +1,6 @@ use crate::{forms::LinkForm, ServerError}; -use super::schema::{links, users}; +use super::schema::{clicks, links, users}; use argonautica::Hasher; use diesel::{Insertable, Queryable}; use dotenv::dotenv; @@ -89,3 +89,31 @@ impl NewLink { } } } + +#[derive(Serialize, Debug, Queryable)] +pub struct Click { + pub id: i32, + pub link: i32, + pub created_at: chrono::NaiveDateTime, +} + +#[derive(Serialize, Insertable)] +#[table_name = "clicks"] +pub struct NewClick { + pub link: i32, + pub created_at: chrono::NaiveDateTime, +} + +impl NewClick { + pub fn new(link_id: i32) -> Self { + Self { + link: link_id, + created_at: chrono::Local::now().naive_utc(), + } + } +} + +#[derive(Serialize, Debug, Queryable)] +pub struct Count { + count: i32, +} diff --git a/src/schema.rs b/src/schema.rs index 38418cf..93e74f5 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,3 +1,11 @@ +table! { + clicks (id) { + id -> Integer, + link -> Integer, + created_at -> Timestamp, + } +} + table! { links (id) { id -> Integer, @@ -18,6 +26,7 @@ table! { } } +joinable!(clicks -> links (link)); joinable!(links -> users (author)); -allow_tables_to_appear_in_same_query!(links, users,); +allow_tables_to_appear_in_same_query!(clicks, links, users,); diff --git a/src/views.rs b/src/views.rs index 0a7ba8d..b98b3c8 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,6 +1,5 @@ use std::time::SystemTime; -use crate::ServerError; use actix_identity::Identity; use actix_web::{ http::header::{CacheControl, CacheDirective, ContentType, Expires}, @@ -14,7 +13,8 @@ use qrcode::{render::svg, QrCode}; use tera::{Context, Tera}; use super::forms::LinkForm; -use super::models::{Link, LoginUser, NewLink, NewUser, User}; +use super::models::{Count, Link, LoginUser, NewClick, NewLink, NewUser, User}; +use crate::ServerError; fn establish_connection() -> Result { dotenv().ok(); @@ -49,11 +49,30 @@ pub(crate) async fn index( tera: web::Data, id: Identity, ) -> Result { - use super::schema::links::dsl::links; - use super::schema::users::dsl::users; + use super::schema::clicks; + use super::schema::links; + use super::schema::users; if let Some(id) = id.identity() { let connection = establish_connection()?; - let all_links: Vec<(Link, User)> = links.inner_join(users).load(&connection)?; + let query = links::dsl::links + .inner_join(users::dsl::users) + .left_join(clicks::dsl::clicks) + .group_by(links::id) + .select(( + ( + links::id, + links::title, + links::target, + links::code, + links::author, + links::created_at, + ), + (users::id, users::username, users::email, users::password), + (diesel::dsl::sql::( + "COUNT(clicks.id)", + ),), + )); + let all_links: Vec<(Link, User, Count)> = query.load(&connection)?; let mut data = Context::new(); data.insert("name", &id); @@ -367,7 +386,16 @@ pub(crate) async fn redirect( let link = links.filter(code.eq(&data.0)).first::(&connection); match link { - Ok(link) => Ok(redirect_builder(&link.target)), + Ok(link) => { + use super::schema::clicks; + let new_click = NewClick::new(link.id); + let connection = establish_connection()?; + + diesel::insert_into(clicks::table) + .values(&new_click) + .execute(&connection)?; + Ok(redirect_builder(&link.target)) + } Err(NotFound) => { let mut data = Context::new(); data.insert("title", "Wurde gelöscht"); diff --git a/templates/index.html b/templates/index.html index 58ba119..ac34a92 100644 --- a/templates/index.html +++ b/templates/index.html @@ -19,10 +19,14 @@ Benutzername + + Statistik + {% for links_user in links_per_users %} {% set l = links_user[0] %} {% set u = links_user[1] %} + {% set c = links_user[2] %} {{l.code}} @@ -37,6 +41,9 @@ {{ u.username }} + + {{ c.count }} + {% endfor %}