From 6d8f93d8a1169fe951935d31568b2bde15b56eeb Mon Sep 17 00:00:00 2001
From: Dessalines <tyhou13@gmx.com>
Date: Thu, 3 Dec 2020 13:39:56 -0500
Subject: [PATCH] More user aggregates.

---
 lemmy_api/src/site.rs                         |  2 +-
 lemmy_db/src/aggregates/mod.rs                |  2 +
 .../src/{ => aggregates}/site_aggregates.rs   |  0
 lemmy_db/src/aggregates/user_aggregates.rs    | 22 +++++++++
 lemmy_db/src/lib.rs                           |  2 +-
 lemmy_db/src/views/site_view.rs               |  8 ++--
 lemmy_db/src/views/user_view.rs               | 48 +++++++++++--------
 lemmy_structs/src/site.rs                     |  2 +-
 .../up.sql                                    | 19 ++++++--
 9 files changed, 73 insertions(+), 32 deletions(-)
 create mode 100644 lemmy_db/src/aggregates/mod.rs
 rename lemmy_db/src/{ => aggregates}/site_aggregates.rs (100%)
 create mode 100644 lemmy_db/src/aggregates/user_aggregates.rs

diff --git a/lemmy_api/src/site.rs b/lemmy_api/src/site.rs
index 2cd97f7f..c6222417 100644
--- a/lemmy_api/src/site.rs
+++ b/lemmy_api/src/site.rs
@@ -10,6 +10,7 @@ use actix_web::web::Data;
 use anyhow::Context;
 use lemmy_apub::fetcher::search_by_apub_id;
 use lemmy_db::{
+  aggregates::site_aggregates::SiteAggregates,
   category::*,
   comment_view::*,
   community_view::*,
@@ -19,7 +20,6 @@ use lemmy_db::{
   naive_now,
   post_view::*,
   site::*,
-  site_aggregates::SiteAggregates,
   user_view::*,
   views::site_view::SiteView,
   Crud,
diff --git a/lemmy_db/src/aggregates/mod.rs b/lemmy_db/src/aggregates/mod.rs
new file mode 100644
index 00000000..2791c977
--- /dev/null
+++ b/lemmy_db/src/aggregates/mod.rs
@@ -0,0 +1,2 @@
+pub mod site_aggregates;
+pub mod user_aggregates;
diff --git a/lemmy_db/src/site_aggregates.rs b/lemmy_db/src/aggregates/site_aggregates.rs
similarity index 100%
rename from lemmy_db/src/site_aggregates.rs
rename to lemmy_db/src/aggregates/site_aggregates.rs
diff --git a/lemmy_db/src/aggregates/user_aggregates.rs b/lemmy_db/src/aggregates/user_aggregates.rs
new file mode 100644
index 00000000..26c2c067
--- /dev/null
+++ b/lemmy_db/src/aggregates/user_aggregates.rs
@@ -0,0 +1,22 @@
+use crate::schema::user_aggregates;
+use diesel::{result::Error, *};
+use serde::Serialize;
+
+#[derive(Queryable, Associations, Identifiable, PartialEq, Debug, Serialize, Clone)]
+#[table_name = "user_aggregates"]
+pub struct UserAggregates {
+  pub id: i32,
+  pub user_id: i32,
+  pub post_count: i64,
+  pub post_score: i64,
+  pub comment_count: i64,
+  pub comment_score: i64,
+}
+
+impl UserAggregates {
+  pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
+    user_aggregates::table.find(id).first::<Self>(conn)
+  }
+}
+
+// TODO add unit tests, to make sure triggers are working
diff --git a/lemmy_db/src/lib.rs b/lemmy_db/src/lib.rs
index c7f4585f..a4600ac4 100644
--- a/lemmy_db/src/lib.rs
+++ b/lemmy_db/src/lib.rs
@@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize};
 use std::{env, env::VarError};
 
 pub mod activity;
+pub mod aggregates;
 pub mod category;
 pub mod comment;
 pub mod comment_report;
@@ -28,7 +29,6 @@ pub mod private_message;
 pub mod private_message_view;
 pub mod schema;
 pub mod site;
-pub mod site_aggregates;
 pub mod user;
 pub mod user_mention;
 pub mod user_mention_view;
diff --git a/lemmy_db/src/views/site_view.rs b/lemmy_db/src/views/site_view.rs
index 9b14056e..547c13b4 100644
--- a/lemmy_db/src/views/site_view.rs
+++ b/lemmy_db/src/views/site_view.rs
@@ -1,5 +1,5 @@
 use crate::{
-  schema::{site as site_table, user_},
+  schema::{site, user_},
   site::Site,
   user::{UserSafe, User_},
 };
@@ -14,13 +14,13 @@ pub struct SiteView {
 
 impl SiteView {
   pub fn read(conn: &PgConnection) -> Result<Self, Error> {
-    let site_join = site_table::table
+    let (site, creator) = site::table
       .inner_join(user_::table)
       .first::<(Site, User_)>(conn)?;
 
     Ok(SiteView {
-      site: site_join.0,
-      creator: site_join.1.to_safe(),
+      site,
+      creator: creator.to_safe(),
     })
   }
 }
diff --git a/lemmy_db/src/views/user_view.rs b/lemmy_db/src/views/user_view.rs
index eb18afbc..33c441be 100644
--- a/lemmy_db/src/views/user_view.rs
+++ b/lemmy_db/src/views/user_view.rs
@@ -1,5 +1,6 @@
 use crate::{
-  schema::user_,
+  aggregates::user_aggregates::UserAggregates,
+  schema::{user_, user_aggregates},
   user::{UserSafe, User_},
 };
 use diesel::{result::Error, *};
@@ -8,58 +9,63 @@ use serde::Serialize;
 #[derive(Debug, Serialize, Clone)]
 pub struct UserViewSafe {
   pub user: UserSafe,
-  // TODO
-  // pub number_of_posts: i64,
-  // pub post_score: i64,
-  // pub number_of_comments: i64,
-  // pub comment_score: i64,
+  pub counts: UserAggregates,
 }
 
+#[derive(Debug, Serialize, Clone)]
 pub struct UserViewDangerous {
   pub user: User_,
-  // TODO
-  // pub number_of_posts: i64,
-  // pub post_score: i64,
-  // pub number_of_comments: i64,
-  // pub comment_score: i64,
+  pub counts: UserAggregates,
 }
 
 impl UserViewDangerous {
   pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
-    let user = user_::table.find(id).first::<User_>(conn)?;
-    Ok(Self { user })
+    let (user, counts) = user_::table
+      .find(id)
+      .inner_join(user_aggregates::table)
+      .first::<(User_, UserAggregates)>(conn)?;
+    Ok(Self { user, counts })
   }
 }
 
 impl UserViewSafe {
   pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
-    let user = user_::table.find(id).first::<User_>(conn)?.to_safe();
-    Ok(Self { user })
+    let (user, counts) = user_::table
+      .find(id)
+      .inner_join(user_aggregates::table)
+      .first::<(User_, UserAggregates)>(conn)?;
+    Ok(Self {
+      user: user.to_safe(),
+      counts,
+    })
   }
 
   pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {
     let admins = user_::table
-      // TODO do joins here
+      .inner_join(user_aggregates::table)
       .filter(user_::admin.eq(true))
       .order_by(user_::published)
-      .load::<User_>(conn)?;
+      .load::<(User_, UserAggregates)>(conn)?;
 
     Ok(vec_to_user_view_safe(admins))
   }
 
   pub fn banned(conn: &PgConnection) -> Result<Vec<Self>, Error> {
     let banned = user_::table
-      // TODO do joins here
+      .inner_join(user_aggregates::table)
       .filter(user_::banned.eq(true))
-      .load::<User_>(conn)?;
+      .load::<(User_, UserAggregates)>(conn)?;
 
     Ok(vec_to_user_view_safe(banned))
   }
 }
 
-fn vec_to_user_view_safe(users: Vec<User_>) -> Vec<UserViewSafe> {
+fn vec_to_user_view_safe(users: Vec<(User_, UserAggregates)>) -> Vec<UserViewSafe> {
   users
     .iter()
-    .map(|a| UserViewSafe { user: a.to_safe() })
+    .map(|a| UserViewSafe {
+      user: a.0.to_safe(),
+      counts: a.1.to_owned(),
+    })
     .collect::<Vec<UserViewSafe>>()
 }
diff --git a/lemmy_structs/src/site.rs b/lemmy_structs/src/site.rs
index 12fda258..dbbb37c4 100644
--- a/lemmy_structs/src/site.rs
+++ b/lemmy_structs/src/site.rs
@@ -1,10 +1,10 @@
 use lemmy_db::{
+  aggregates::site_aggregates::SiteAggregates,
   category::*,
   comment_view::*,
   community_view::*,
   moderator_views::*,
   post_view::*,
-  site_aggregates::SiteAggregates,
   user::*,
   user_view::*,
   views::site_view::SiteView,
diff --git a/migrations/2020-12-03-035643_create_user_aggregates/up.sql b/migrations/2020-12-03-035643_create_user_aggregates/up.sql
index 85a0b675..8d46fbfb 100644
--- a/migrations/2020-12-03-035643_create_user_aggregates/up.sql
+++ b/migrations/2020-12-03-035643_create_user_aggregates/up.sql
@@ -60,11 +60,17 @@ returns trigger language plpgsql
 as $$
 begin
   IF (TG_OP = 'INSERT') THEN
+    -- TODO not sure if this is working right
+    -- Need to get the post creator, not the voter
     update user_aggregates 
-    set post_score = post_score + NEW.score where user_id = NEW.user_id;
+    set post_score = post_score + NEW.score
+    from post_like pl join post p on p.id = pl.post_id
+    where p.id = NEW.post_id and p.creator_id = NEW.user_id;
   ELSIF (TG_OP = 'DELETE') THEN
     update user_aggregates 
-    set post_score = post_score - OLD.score where user_id = OLD.user_id;
+    set post_score = post_score - OLD.score
+    from post_like pl join post p on p.id = pl.post_id
+    where p.id = OLD.post_id and p.creator_id = OLD.user_id;
   END IF;
   return null;
 end $$;
@@ -98,11 +104,16 @@ returns trigger language plpgsql
 as $$
 begin
   IF (TG_OP = 'INSERT') THEN
+    -- Need to get the post creator, not the voter
     update user_aggregates 
-    set comment_score = comment_score + NEW.score where user_id = NEW.user_id;
+    set comment_score = comment_score + NEW.score
+    from comment_like pl join comment p on p.id = pl.comment_id
+    where p.id = NEW.comment_id and p.creator_id = NEW.user_id;
   ELSIF (TG_OP = 'DELETE') THEN
     update user_aggregates 
-    set comment_score = comment_score - OLD.score where user_id = OLD.user_id;
+    set comment_score = comment_score - OLD.score
+    from comment_like pl join comment p on p.id = pl.comment_id
+    where p.id = OLD.comment_id and p.creator_id = OLD.user_id;
   END IF;
   return null;
 end $$;
-- 
2.44.1