add: great post average
This commit is contained in:
parent
11fb131b8b
commit
15dc2e5d71
5 changed files with 122 additions and 0 deletions
|
@ -160,6 +160,14 @@
|
|||
(text "Posts"))
|
||||
(span
|
||||
(text "{{ profile.post_count }}")))
|
||||
(div
|
||||
("class" "w-full flex justify-between items-center")
|
||||
("title" "great post average (limited time)")
|
||||
(span
|
||||
("class" "notification chip")
|
||||
(text "GPA 🐇"))
|
||||
(span
|
||||
(text "{{ gpa }}")))
|
||||
(text "{% if not profile.settings.private_last_seen or is_self or is_helper %}")
|
||||
(div
|
||||
("class" "w-full flex justify-between items-center")
|
||||
|
|
|
@ -645,3 +645,33 @@ pub async fn post_to_socket_request(
|
|||
payload: (),
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculate the user's great post average.
|
||||
pub async fn get_user_gpa_request(
|
||||
jar: CookieJar,
|
||||
Path(id): Path<usize>,
|
||||
Extension(data): Extension<State>,
|
||||
) -> impl IntoResponse {
|
||||
let data = &(data.read().await).0;
|
||||
let user = match get_user_from_token!(jar, data) {
|
||||
Some(ua) => ua,
|
||||
None => return Json(Error::NotAllowed.into()),
|
||||
};
|
||||
|
||||
if !user.permissions.check(FinePermission::MANAGE_USERS) {
|
||||
return Json(Error::NotAllowed.into());
|
||||
}
|
||||
|
||||
let gpa = data.calculate_user_gpa(id).await;
|
||||
return Json(ApiReturn {
|
||||
ok: true,
|
||||
message: if gpa >= 3 {
|
||||
"cool".to_string()
|
||||
} else if gpa >= 4 {
|
||||
"extraordinary".to_string()
|
||||
} else {
|
||||
"ok".to_string()
|
||||
},
|
||||
payload: Some(gpa),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -221,6 +221,10 @@ pub fn routes() -> Router {
|
|||
get(auth::profile::redirect_from_ip),
|
||||
)
|
||||
.route("/auth/ip/{ip}/block", post(auth::social::ip_block_request))
|
||||
.route(
|
||||
"/auth/user/{id}/gpa",
|
||||
get(auth::profile::get_user_gpa_request),
|
||||
)
|
||||
.route(
|
||||
"/auth/user/{id}/_connect/{stream}",
|
||||
any(auth::profile::subscription_handler),
|
||||
|
|
|
@ -350,6 +350,7 @@ pub async fn posts_request(
|
|||
context.insert("pinned", &pinned);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("tag", &props.tag);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&user,
|
||||
|
@ -453,6 +454,7 @@ pub async fn replies_request(
|
|||
|
||||
context.insert("posts", &posts);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&user,
|
||||
|
@ -558,6 +560,7 @@ pub async fn media_request(
|
|||
|
||||
context.insert("posts", &posts);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&user,
|
||||
|
@ -652,6 +655,7 @@ pub async fn outbox_request(
|
|||
|
||||
context.insert("questions", &questions);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&Some(user),
|
||||
|
@ -746,6 +750,7 @@ pub async fn following_request(
|
|||
|
||||
context.insert("list", &list);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&user,
|
||||
|
@ -840,6 +845,7 @@ pub async fn followers_request(
|
|||
|
||||
context.insert("list", &list);
|
||||
context.insert("page", &props.page);
|
||||
context.insert("gpa", &data.0.calculate_user_gpa(other_user.id).await);
|
||||
profile_context(
|
||||
&mut context,
|
||||
&user,
|
||||
|
|
|
@ -510,6 +510,80 @@ impl DataManager {
|
|||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Calculate the GPA (great post average) of a given user.
|
||||
///
|
||||
/// To be considered a "great post", a post must have a score ((likes - dislikes) / (likes + dislikes))
|
||||
/// of at least 0.6.
|
||||
///
|
||||
/// GPA is calculated based on the user's last 250 posts.
|
||||
pub async fn calculate_user_gpa(&self, id: usize) -> usize {
|
||||
// just for note, this is SUPER bad for performance... which is why we
|
||||
// only calculate this when it expires in the cache (every week)
|
||||
if let Some(cached) = self.0.1.get(format!("atto.user.gpa:{}", id)).await {
|
||||
if let Ok(c) = cached.parse() {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
let conn = match self.0.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
|
||||
let res = query_rows!(
|
||||
&conn,
|
||||
&format!("SELECT * FROM posts WHERE owner = $1 ORDER BY created DESC LIMIT 250",),
|
||||
&[&(id as i64)],
|
||||
|x| { Self::get_post_from_row(x) }
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ...
|
||||
let mut real_posts_count: usize = 0; // posts which can be scored
|
||||
let mut good_posts: usize = 0;
|
||||
// let mut bad_posts: usize = 0;
|
||||
|
||||
let posts = res.unwrap();
|
||||
|
||||
for post in posts {
|
||||
if post.likes == 0 && post.dislikes == 0 {
|
||||
// post has no likes or dislikes... doesn't count
|
||||
continue;
|
||||
}
|
||||
|
||||
real_posts_count += 1;
|
||||
|
||||
// likes percentage / total likes
|
||||
let score: f32 = (post.likes as f32 - post.dislikes as f32)
|
||||
/ (post.likes as f32 + post.dislikes as f32);
|
||||
|
||||
if score.is_sign_negative() {
|
||||
// bad_posts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if score > 0.6 {
|
||||
good_posts += 1;
|
||||
}
|
||||
// } else {
|
||||
// bad_posts += 1;
|
||||
// }
|
||||
}
|
||||
|
||||
let gpa = ((good_posts as f32 / real_posts_count as f32) * 4.0).round() as usize;
|
||||
|
||||
self.0
|
||||
.1
|
||||
.set(format!("atto.user.gpa:{}", id), gpa.to_string())
|
||||
.await;
|
||||
|
||||
gpa
|
||||
}
|
||||
|
||||
/// Get all replies from the given user (from most recent).
|
||||
///
|
||||
/// # Arguments
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue