added stats page for referrals (shared, and received) (#95)

* added stats page for referrals (shared, and received)

* removed referrals address, and return ok response if no referrals used

* merged from devel

* changed unwrap for context

* changes from PR
This commit is contained in:
David 2023-06-07 18:38:19 +02:00 committed by GitHub
parent 172baabb4e
commit fd69e6acdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 143 additions and 1 deletions

@ -189,6 +189,14 @@ pub async fn serve(
"/user/referral",
get(users::referral::user_referral_link_get),
)
.route(
"/user/referral/stats/used-codes",
get(users::referral::user_used_referral_stats),
)
.route(
"/user/referral/stats/shared-codes",
get(users::referral::user_shared_referral_stats),
)
.route("/user/revert_logs", get(users::stats::user_revert_logs_get))
.route(
"/user/stats/aggregate",

@ -1,6 +1,7 @@
//! Handle registration, logins, and managing account data.
use crate::app::Web3ProxyApp;
use crate::errors::Web3ProxyResponse;
use crate::frontend::users::referral;
use crate::referral_code::ReferralCode;
use anyhow::Context;
use axum::{
@ -10,14 +11,18 @@ use axum::{
Extension, Json, TypedHeader,
};
use axum_macros::debug_handler;
use entities::referrer;
use entities::{referee, referrer, user};
use ethers::types::Address;
use hashbrown::HashMap;
use http::StatusCode;
use migration::sea_orm;
use migration::sea_orm::prelude::{DateTime, Decimal};
use migration::sea_orm::ActiveModelTrait;
use migration::sea_orm::ColumnTrait;
use migration::sea_orm::EntityTrait;
use migration::sea_orm::QueryFilter;
use num_traits::one;
use serde::Serialize;
use serde_json::json;
use std::sync::Arc;
@ -70,3 +75,132 @@ pub async fn user_referral_link_get(
let response = (status_code, Json(response_json)).into_response();
Ok(response)
}
#[debug_handler]
pub async fn user_used_referral_stats(
Extension(app): Extension<Arc<Web3ProxyApp>>,
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
Query(_params): Query<HashMap<String, String>>,
) -> Web3ProxyResponse {
// First get the bearer token and check if the user is logged in
let (user, _semaphore) = app.bearer_is_authorized(bearer).await?;
let db_replica = app
.db_replica()
.context("getting replica db for user's revert logs")?;
// Get all referral records associated with this user
let referrals = referee::Entity::find()
.filter(referee::Column::UserId.eq(user.id))
.find_also_related(referrer::Entity)
.all(db_replica.as_ref())
.await?;
// For each related referral person, find the corresponding user-address
#[derive(Debug, Serialize)]
struct Info {
credits_applied_for_referee: bool,
credits_applied_for_referrer: Decimal,
referral_start_date: DateTime,
used_referral_code: String,
};
let mut out: Vec<Info> = Vec::new();
for x in referrals.into_iter() {
let (referral_record, referrer_record) = (x.0, x.1.context("each referral entity should have a referral code associated with it, but this is not the case!")?);
// The foreign key is never optional
let referring_user = user::Entity::find_by_id(referrer_record.user_id)
.one(db_replica.as_ref())
.await?
.context("Database error, no foreign key found for referring user")?;
let tmp = Info {
credits_applied_for_referee: referral_record.credits_applied_for_referee,
credits_applied_for_referrer: referral_record.credits_applied_for_referrer,
referral_start_date: referral_record.referral_start_date,
used_referral_code: referrer_record.referral_code,
};
// Start inserting json's into this
out.push(tmp);
}
// Turn this into a response
let response_json = json!({
"referrals": out,
"user": user,
});
let response = (StatusCode::OK, Json(response_json)).into_response();
Ok(response)
}
#[debug_handler]
pub async fn user_shared_referral_stats(
Extension(app): Extension<Arc<Web3ProxyApp>>,
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
Query(_params): Query<HashMap<String, String>>,
) -> Web3ProxyResponse {
// First get the bearer token and check if the user is logged in
let (user, _semaphore) = app.bearer_is_authorized(bearer).await?;
let db_replica = app
.db_replica()
.context("getting replica db for user's revert logs")?;
// Get all referral records associated with this user
let referrals = referrer::Entity::find()
.filter(referrer::Column::UserId.eq(user.id))
.find_also_related(referee::Entity)
.all(db_replica.as_ref())
.await?;
// Return early if the user does not have any referred entities
if referrals.len() == 0 {
let response_json = json!({
"referrals": [],
"used_referral_code": None::<()>,
"user": user,
});
let response = (StatusCode::OK, Json(response_json)).into_response();
return Ok(response);
}
// For each related referral person, find the corresponding user-address
#[derive(Debug, Serialize)]
struct Info {
credits_applied_for_referee: bool,
credits_applied_for_referrer: Decimal,
referral_start_date: DateTime,
referred_address: Address,
};
let mut out: Vec<Info> = Vec::new();
let mut used_referral_code = "".to_owned(); // This is only for safety purposes, because of the condition above we always know that there is at least one record
for x in referrals.into_iter() {
let (referrer, referral_record) = (x.0, x.1.context("each referral code should have a referee associated with it (that's what we query), but this is not the case!")?);
used_referral_code = referrer.referral_code;
// The foreign key is never optional
let referred_user = user::Entity::find_by_id(referral_record.user_id)
.one(db_replica.as_ref())
.await?
.context("Database error, no foreign key found for referring user")?;
let tmp = Info {
credits_applied_for_referee: referral_record.credits_applied_for_referee,
credits_applied_for_referrer: referral_record.credits_applied_for_referrer,
referral_start_date: referral_record.referral_start_date,
referred_address: Address::from_slice(&referred_user.address),
};
// Start inserting json's into this
out.push(tmp);
}
// Turn this into a response
let response_json = json!({
"referrals": out,
"used_referral_code": used_referral_code,
"user": user,
});
let response = (StatusCode::OK, Json(response_json)).into_response();
Ok(response)
}