Skip to content

Commit

Permalink
✨ 站点排行
Browse files Browse the repository at this point in the history
  • Loading branch information
naiba committed Aug 24, 2023
1 parent 0a99a67 commit 9c1b9c0
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,26 @@ version = "0.1.0"
[dependencies]
anyhow = "1.0.44"
askama = "0.11.0"
axum = {version = "0.4.4", features = ["headers", "ws"]}
chrono = "0.4"
axum = { version = "0.4.4", features = ["headers", "ws"] }
chrono = { version = "0.4", features = ["serde"] }
chrono-tz = "0.6.1"
diesel = {version = "2.0.0-rc.0", features = ["bigdecimal", "chrono", "r2d2", "sqlite"]}
diesel = { version = "2.0.0-rc.0", features = [
"bigdecimal",
"chrono",
"r2d2",
"sqlite",
] }
diesel_migrations = "2.0.0-rc.0"
dotenv = "0.15.0"
headers = "0.3.5"
lazy_static = "1.4.0"
r-cache = "0.4.4"
regex = "1.5.4"
serde = {version = "1.0", features = ["derive"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = {version = "1.0", features = ["full"]}
tokio = { version = "1.0", features = ["full"] }
tracing = "0.1"
tracing-subscriber = {version = "0.3", features = ["env-filter"]}
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
url = "2.2.2"

[dependencies.libsqlite3-sys]
Expand Down
18 changes: 18 additions & 0 deletions src/app_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub struct Context {
pub visitor_tx: Sender<String>,
pub visitor_rx: Receiver<String>,

pub rank: RwLock<Vec<Statistics>>,

pub cache: r_cache::cache::Cache<String, ()>,
}

Expand Down Expand Up @@ -164,6 +166,13 @@ impl Context {
domain2id.insert(v.domain.clone(), k.clone());
});

let rank = Statistics::rank_between(
db_pool.get().unwrap(),
NaiveDateTime::from_timestamp(0, 0),
now_shanghai(),
)
.unwrap();

let (visitor_tx, visitor_rx) = watch::channel::<String>("".to_string());

let rank_svg = Statistics::prev_day_rank_avg(db_pool.get().unwrap());
Expand All @@ -177,6 +186,7 @@ impl Context {
unique_visitor: RwLock::new(page_view),
referrer: RwLock::new(referrer),
rank_svg: RwLock::new(rank_svg),
rank: RwLock::new(rank),

domain2id: domain2id,
id2member: membership,
Expand Down Expand Up @@ -248,6 +258,14 @@ impl Context {
}
drop(uv_write);
drop(referrer_write);

let mut rank = self.rank.write().await;
*rank = Statistics::rank_between(
self.db_pool.get().unwrap(),
NaiveDateTime::from_timestamp(0, 0),
now_shanghai(),
)
.unwrap_or_default();
}
}
}
31 changes: 29 additions & 2 deletions src/app_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use tokio::select;
use crate::{
app_model::{Context, DynContext},
boring_face::BoringFace,
membership_model::Membership,
GIT_HASH,
membership_model::{Membership, RankAndMembership},
now_shanghai, GIT_HASH,
};

pub async fn ws_upgrade(
Expand Down Expand Up @@ -125,6 +125,8 @@ struct HomeTemplate {
membership: Vec<Membership>,
uv: HashMap<i64, i64>,
referrer: HashMap<i64, i64>,
rank: Vec<RankAndMembership>,
to_be_remove: Vec<RankAndMembership>,
level: HashMap<i64, i64>,
}

Expand Down Expand Up @@ -165,10 +167,35 @@ pub async fn home_page(
membership.push(ctx.id2member.get(&v.0).unwrap().to_owned());
}

let rank = ctx.rank.read().await.to_owned();

let mut rank_and_membership_to_be_remove = Vec::new();
let mut rank_and_membership = Vec::new();

rank.iter()
.filter(|r| ctx.id2member.contains_key(&r.membership_id))
.for_each(|r| {
if r.updated_at > now_shanghai() - chrono::Duration::days(30) {
let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned();
rank_and_membership.push(RankAndMembership {
rank: r.to_owned(),
membership: m,
});
} else {
let m = ctx.id2member.get(&r.membership_id).unwrap().to_owned();
rank_and_membership_to_be_remove.push(RankAndMembership {
rank: r.to_owned(),
membership: m,
});
}
});

let tpl = HomeTemplate {
membership,
uv: uv_read.clone(),
referrer: referrer_read.clone(),
rank: rank_and_membership,
to_be_remove: rank_and_membership_to_be_remove,
level,
version: GIT_HASH[0..8].to_string(),
};
Expand Down
10 changes: 9 additions & 1 deletion src/membership_model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Clone, Serialize)]
use crate::statistics_model::Statistics;

#[derive(Deserialize, Clone, Debug, Serialize)]
pub struct Membership {
#[serde(skip_deserializing)]
pub id: i64,
Expand All @@ -10,3 +12,9 @@ pub struct Membership {
pub description: String,
pub github_username: String,
}

#[derive(Deserialize, Clone, Serialize)]
pub struct RankAndMembership {
pub rank: Statistics,
pub membership: Membership,
}
60 changes: 59 additions & 1 deletion src/statistics_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use crate::now_shanghai;
use crate::schema::statistics::{self, dsl::*};
use anyhow::anyhow;
use chrono::{Duration, NaiveDateTime, NaiveTime};
use diesel::dsl::sql;
use diesel::r2d2::{ConnectionManager, PooledConnection};
use diesel::sqlite::Sqlite;
use diesel::{debug_query, prelude::*};
use diesel::{Queryable, SqliteConnection};

#[derive(Queryable, Debug, Clone, Insertable)]
#[derive(Queryable, Debug, Clone, Insertable, serde::Serialize, serde::Deserialize)]
#[diesel(table_name = statistics)]
pub struct Statistics {
pub id: i32,
Expand Down Expand Up @@ -79,6 +80,63 @@ impl Statistics {
1
}

pub fn rank_between(
mut conn: PooledConnection<ConnectionManager<SqliteConnection>>,
start: NaiveDateTime,
end: NaiveDateTime,
) -> Result<Vec<Statistics>, anyhow::Error> {
let res = statistics
.select((
sql::<diesel::sql_types::BigInt>("SUM(membership_id) as membership_id"),
sql::<diesel::sql_types::Timestamp>("MIN(created_at) as created_at"),
sql::<diesel::sql_types::BigInt>("SUM(unique_visitor) as unique_visitor"),
sql::<diesel::sql_types::BigInt>("SUM(referrer) as referrer"),
))
.filter(created_at.between(start, end))
.group_by(membership_id)
.order(sql::<diesel::sql_types::BigInt>(
"unique_visitor + referrer*1.5",
))
.load::<(i64, NaiveDateTime, i64, i64)>(&mut conn);

let updated_at_list = statistics
.select((
membership_id,
sql::<diesel::sql_types::Timestamp>("MAX(updated_at) as m_updated_at"),
))
.filter(unique_visitor.gt(0).or(referrer.gt(0)))
.group_by(membership_id)
.order(sql::<diesel::sql_types::BigInt>("m_updated_at"))
.load::<(i64, NaiveDateTime)>(&mut conn);

let id_to_updated_at = updated_at_list
.unwrap_or(Vec::new())
.iter()
.map(|s| (s.0, s.1))
.collect::<std::collections::HashMap<i64, NaiveDateTime>>();

match res {
Ok(all) => {
let mut result = Vec::new();
all.iter().for_each(|s| {
result.push(Statistics {
id: 0,
created_at: s.1,
updated_at: id_to_updated_at
.get(&s.0)
.unwrap_or(&NaiveDateTime::from_timestamp(0, 0))
.to_owned(),
membership_id: s.0,
unique_visitor: s.2,
referrer: s.3,
})
});
Ok(result)
}
Err(e) => Err(anyhow!("{:?}", e)),
}
}

pub fn all(
mut conn: PooledConnection<ConnectionManager<SqliteConnection>>,
) -> Result<Vec<Statistics>, anyhow::Error> {
Expand Down
76 changes: 75 additions & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

{% block content %}
<div class="content">
<h2 class="font-size-18 text-center">会员列表</h2>
<h2 class="font-size-18 text-center">今日存活</h2>
<div class="d-flex flex-wrap member-list">
{% for m in membership %}
<div class="w-md-quarter p-5">
Expand Down Expand Up @@ -34,6 +34,80 @@ <h2 class="font-size-18 text-center">会员列表</h2>
</div>
{% endfor %}
</div>


<h2 class="font-size-18 text-center">成员排行</h2>
<div class="card table-responsive specific-w-300 mw-100 mx-auto rounded-0">
<table class="table">
<thead>
<tr>
<th scope="col">排名</th>
<th scope="col">站点</th>
<th scope="col">UV</th>
<th scope="col">RV</th>
<th scope="col">加入时间</th>
<th scope="col">最后活跃</th>
</tr>
</thead>
<tbody>
{% for r in rank %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>
<a target="_blank" class="text-reset font-weight-bolder"
href="https://{{ r.membership.domain }}" title="{{ r.membership.name|e }}">
{{ r.membership.name|e }}
</a>
</td>
<td>{{ r.rank.unique_visitor }}</td>
<td>{{ r.rank.referrer }}</td>
<td>{{ r.rank.created_at }}</td>
<td>{{ r.rank.updated_at }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

<h2 class="font-size-18 text-center">即将移除</h2>
<div class="card table-responsive specific-w-300 mw-100 mx-auto rounded-0">
<table class="table">
<thead>
<tr>
<th scope="col">编号</th>
<th scope="col">站点</th>
<th scope="col">UV</th>
<th scope="col">RV</th>
<th scope="col">加入时间</th>
<th scope="col">最后活跃</th>
</tr>
</thead>
<tbody>
{% for r in to_be_remove %}
<tr>
<th scope="row">
<del>
{{ loop.index }}
</del>
</th>
<td>
<del>
<a target="_blank" class="text-reset font-weight-bolder"
href="https://{{ r.membership.domain }}" title="{{ r.membership.name|e }}">
{{ r.membership.name|e }}
</a>
</del>
</td>
<td><del>{{ r.rank.unique_visitor }}</del></td>
<td><del>{{ r.rank.referrer }}</del></td>
<td><del>{{ r.rank.created_at }}</del></td>
<td><del>{{ r.rank.updated_at }}</del></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

<div class="text-center font-size-14 mt-10">
(排名按照今日站点流量由高至低,当日无访客将被<b>隐藏</b>
<br>
Expand Down

0 comments on commit 9c1b9c0

Please sign in to comment.