mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 21:13:02 +00:00
🎨 Tweak some names dealing with user activity
* Rename `project_activity_summary` to `top_users_activity_summary` to make clearer the distinction between it and the per-user summary. * Rename `user_activity_summary` to `user_activity_timeline`, since its output is structured a bit differently than the courser-grained "summary" returned by the top-user query. * Rename `ActivityDuration` -> `ActivityPeriod`
This commit is contained in:
parent
5cc5e15f4d
commit
1d10e45282
3 changed files with 60 additions and 49 deletions
|
@ -36,8 +36,8 @@ pub fn routes(rpc_server: &Arc<rpc::Server>, state: Arc<AppState>) -> Router<Bod
|
|||
.route("/panic", post(trace_panic))
|
||||
.route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
|
||||
.route(
|
||||
"/project_activity_summary",
|
||||
get(get_project_activity_summary),
|
||||
"/user_activity/summary",
|
||||
get(get_top_users_activity_summary),
|
||||
)
|
||||
.route("/project_metadata", get(get_project_metadata))
|
||||
.layer(
|
||||
|
@ -264,20 +264,20 @@ async fn get_rpc_server_snapshot(
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GetProjectActivityParams {
|
||||
struct TimePeriodParams {
|
||||
#[serde(with = "time::serde::iso8601")]
|
||||
start: OffsetDateTime,
|
||||
#[serde(with = "time::serde::iso8601")]
|
||||
end: OffsetDateTime,
|
||||
}
|
||||
|
||||
async fn get_project_activity_summary(
|
||||
Query(params): Query<GetProjectActivityParams>,
|
||||
async fn get_top_users_activity_summary(
|
||||
Query(params): Query<TimePeriodParams>,
|
||||
Extension(app): Extension<Arc<AppState>>,
|
||||
) -> Result<ErasedJson> {
|
||||
let summary = app
|
||||
.db
|
||||
.summarize_project_activity(params.start..params.end, 100)
|
||||
.get_top_users_activity_summary(params.start..params.end, 100)
|
||||
.await?;
|
||||
Ok(ErasedJson::pretty(summary))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use axum::http::StatusCode;
|
|||
use collections::HashMap;
|
||||
use futures::StreamExt;
|
||||
use nanoid::nanoid;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use sqlx::postgres::PgPoolOptions as DbOptions;
|
||||
use sqlx::{types::Uuid, FromRow, QueryBuilder, Row};
|
||||
use time::{OffsetDateTime, PrimitiveDateTime};
|
||||
|
@ -63,7 +63,7 @@ pub trait Db: Send + Sync {
|
|||
|
||||
/// Record which users have been active in which projects during
|
||||
/// a given period of time.
|
||||
async fn record_project_activity(
|
||||
async fn record_user_activity(
|
||||
&self,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
active_projects: &[(UserId, ProjectId)],
|
||||
|
@ -71,18 +71,18 @@ pub trait Db: Send + Sync {
|
|||
|
||||
/// Get the users that have been most active during the given time period,
|
||||
/// along with the amount of time they have been active in each project.
|
||||
async fn summarize_project_activity(
|
||||
async fn get_top_users_activity_summary(
|
||||
&self,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
max_user_count: usize,
|
||||
) -> Result<Vec<UserActivitySummary>>;
|
||||
|
||||
/// Get the project activity for the given user and time period.
|
||||
async fn summarize_user_activity(
|
||||
async fn get_user_activity_timeline(
|
||||
&self,
|
||||
user_id: UserId,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
) -> Result<Vec<UserActivityDuration>>;
|
||||
user_id: UserId,
|
||||
) -> Result<Vec<UserActivityPeriod>>;
|
||||
|
||||
async fn get_contacts(&self, id: UserId) -> Result<Vec<Contact>>;
|
||||
async fn has_contact(&self, user_id_a: UserId, user_id_b: UserId) -> Result<bool>;
|
||||
|
@ -564,7 +564,7 @@ impl Db for PostgresDb {
|
|||
Ok(extension_counts)
|
||||
}
|
||||
|
||||
async fn record_project_activity(
|
||||
async fn record_user_activity(
|
||||
&self,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
projects: &[(UserId, ProjectId)],
|
||||
|
@ -593,7 +593,7 @@ impl Db for PostgresDb {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn summarize_project_activity(
|
||||
async fn get_top_users_activity_summary(
|
||||
&self,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
max_user_count: usize,
|
||||
|
@ -648,11 +648,11 @@ impl Db for PostgresDb {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
async fn summarize_user_activity(
|
||||
async fn get_user_activity_timeline(
|
||||
&self,
|
||||
user_id: UserId,
|
||||
time_period: Range<OffsetDateTime>,
|
||||
) -> Result<Vec<UserActivityDuration>> {
|
||||
user_id: UserId,
|
||||
) -> Result<Vec<UserActivityPeriod>> {
|
||||
const COALESCE_THRESHOLD: Duration = Duration::from_secs(5);
|
||||
|
||||
let query = "
|
||||
|
@ -689,19 +689,19 @@ impl Db for PostgresDb {
|
|||
.bind(time_period.end)
|
||||
.fetch(&self.pool);
|
||||
|
||||
let mut durations: HashMap<ProjectId, Vec<UserActivityDuration>> = Default::default();
|
||||
let mut time_periods: HashMap<ProjectId, Vec<UserActivityPeriod>> = Default::default();
|
||||
while let Some(row) = rows.next().await {
|
||||
let (ended_at, duration_millis, project_id, extension, extension_count) = row?;
|
||||
let ended_at = ended_at.assume_utc();
|
||||
let duration = Duration::from_millis(duration_millis as u64);
|
||||
let started_at = ended_at - duration;
|
||||
let project_durations = durations.entry(project_id).or_default();
|
||||
let project_time_periods = time_periods.entry(project_id).or_default();
|
||||
|
||||
if let Some(prev_duration) = project_durations.last_mut() {
|
||||
if let Some(prev_duration) = project_time_periods.last_mut() {
|
||||
if started_at - prev_duration.end <= COALESCE_THRESHOLD {
|
||||
prev_duration.end = ended_at;
|
||||
} else {
|
||||
project_durations.push(UserActivityDuration {
|
||||
project_time_periods.push(UserActivityPeriod {
|
||||
project_id,
|
||||
start: started_at,
|
||||
end: ended_at,
|
||||
|
@ -709,7 +709,7 @@ impl Db for PostgresDb {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
project_durations.push(UserActivityDuration {
|
||||
project_time_periods.push(UserActivityPeriod {
|
||||
project_id,
|
||||
start: started_at,
|
||||
end: ended_at,
|
||||
|
@ -718,7 +718,7 @@ impl Db for PostgresDb {
|
|||
}
|
||||
|
||||
if let Some((extension, extension_count)) = extension.zip(extension_count) {
|
||||
project_durations
|
||||
project_time_periods
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.extensions
|
||||
|
@ -726,7 +726,7 @@ impl Db for PostgresDb {
|
|||
}
|
||||
}
|
||||
|
||||
let mut durations = durations.into_values().flatten().collect::<Vec<_>>();
|
||||
let mut durations = time_periods.into_values().flatten().collect::<Vec<_>>();
|
||||
durations.sort_unstable_by_key(|duration| duration.start);
|
||||
Ok(durations)
|
||||
}
|
||||
|
@ -1206,7 +1206,18 @@ impl Db for PostgresDb {
|
|||
macro_rules! id_type {
|
||||
($name:ident) => {
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Serialize,
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
sqlx::Type,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[sqlx(transparent)]
|
||||
#[serde(transparent)]
|
||||
|
@ -1263,7 +1274,7 @@ pub struct UserActivitySummary {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub struct UserActivityDuration {
|
||||
pub struct UserActivityPeriod {
|
||||
project_id: ProjectId,
|
||||
start: OffsetDateTime,
|
||||
end: OffsetDateTime,
|
||||
|
@ -1549,24 +1560,24 @@ pub mod tests {
|
|||
|
||||
// User 2 opens a project
|
||||
let t1 = t0 + Duration::from_secs(10);
|
||||
db.record_project_activity(t0..t1, &[(user_2, project_2)])
|
||||
db.record_user_activity(t0..t1, &[(user_2, project_2)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let t2 = t1 + Duration::from_secs(10);
|
||||
db.record_project_activity(t1..t2, &[(user_2, project_2)])
|
||||
db.record_user_activity(t1..t2, &[(user_2, project_2)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// User 1 joins the project
|
||||
let t3 = t2 + Duration::from_secs(10);
|
||||
db.record_project_activity(t2..t3, &[(user_2, project_2), (user_1, project_2)])
|
||||
db.record_user_activity(t2..t3, &[(user_2, project_2), (user_1, project_2)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// User 1 opens another project
|
||||
let t4 = t3 + Duration::from_secs(10);
|
||||
db.record_project_activity(
|
||||
db.record_user_activity(
|
||||
t3..t4,
|
||||
&[
|
||||
(user_2, project_2),
|
||||
|
@ -1579,7 +1590,7 @@ pub mod tests {
|
|||
|
||||
// User 3 joins that project
|
||||
let t5 = t4 + Duration::from_secs(10);
|
||||
db.record_project_activity(
|
||||
db.record_user_activity(
|
||||
t4..t5,
|
||||
&[
|
||||
(user_2, project_2),
|
||||
|
@ -1593,18 +1604,18 @@ pub mod tests {
|
|||
|
||||
// User 2 leaves
|
||||
let t6 = t5 + Duration::from_secs(5);
|
||||
db.record_project_activity(t5..t6, &[(user_1, project_1), (user_3, project_1)])
|
||||
db.record_user_activity(t5..t6, &[(user_1, project_1), (user_3, project_1)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let t7 = t6 + Duration::from_secs(60);
|
||||
let t8 = t7 + Duration::from_secs(10);
|
||||
db.record_project_activity(t7..t8, &[(user_1, project_1)])
|
||||
db.record_user_activity(t7..t8, &[(user_1, project_1)])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
db.summarize_project_activity(t0..t6, 10).await.unwrap(),
|
||||
db.get_top_users_activity_summary(t0..t6, 10).await.unwrap(),
|
||||
&[
|
||||
UserActivitySummary {
|
||||
id: user_1,
|
||||
|
@ -1627,15 +1638,15 @@ pub mod tests {
|
|||
]
|
||||
);
|
||||
assert_eq!(
|
||||
db.summarize_user_activity(user_1, t3..t6).await.unwrap(),
|
||||
db.get_user_activity_timeline(t3..t6, user_1).await.unwrap(),
|
||||
&[
|
||||
UserActivityDuration {
|
||||
UserActivityPeriod {
|
||||
project_id: project_1,
|
||||
start: t3,
|
||||
end: t6,
|
||||
extensions: HashMap::from_iter([("rs".to_string(), 5), ("md".to_string(), 7)]),
|
||||
},
|
||||
UserActivityDuration {
|
||||
UserActivityPeriod {
|
||||
project_id: project_2,
|
||||
start: t3,
|
||||
end: t5,
|
||||
|
@ -1644,21 +1655,21 @@ pub mod tests {
|
|||
]
|
||||
);
|
||||
assert_eq!(
|
||||
db.summarize_user_activity(user_1, t0..t8).await.unwrap(),
|
||||
db.get_user_activity_timeline(t0..t8, user_1).await.unwrap(),
|
||||
&[
|
||||
UserActivityDuration {
|
||||
UserActivityPeriod {
|
||||
project_id: project_2,
|
||||
start: t2,
|
||||
end: t5,
|
||||
extensions: Default::default(),
|
||||
},
|
||||
UserActivityDuration {
|
||||
UserActivityPeriod {
|
||||
project_id: project_1,
|
||||
start: t3,
|
||||
end: t6,
|
||||
extensions: HashMap::from_iter([("rs".to_string(), 5), ("md".to_string(), 7)]),
|
||||
},
|
||||
UserActivityDuration {
|
||||
UserActivityPeriod {
|
||||
project_id: project_1,
|
||||
start: t7,
|
||||
end: t8,
|
||||
|
@ -2450,27 +2461,27 @@ pub mod tests {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn record_project_activity(
|
||||
async fn record_user_activity(
|
||||
&self,
|
||||
_period: Range<OffsetDateTime>,
|
||||
_time_period: Range<OffsetDateTime>,
|
||||
_active_projects: &[(UserId, ProjectId)],
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn summarize_project_activity(
|
||||
async fn get_top_users_activity_summary(
|
||||
&self,
|
||||
_period: Range<OffsetDateTime>,
|
||||
_time_period: Range<OffsetDateTime>,
|
||||
_limit: usize,
|
||||
) -> Result<Vec<UserActivitySummary>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn summarize_user_activity(
|
||||
async fn get_user_activity_timeline(
|
||||
&self,
|
||||
_user_id: UserId,
|
||||
_time_period: Range<OffsetDateTime>,
|
||||
) -> Result<Vec<UserActivityDuration>> {
|
||||
_user_id: UserId,
|
||||
) -> Result<Vec<UserActivityPeriod>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
|
|
@ -332,7 +332,7 @@ impl Server {
|
|||
let period_end = OffsetDateTime::now_utc();
|
||||
this.app_state
|
||||
.db
|
||||
.record_project_activity(period_start..period_end, &active_projects)
|
||||
.record_user_activity(period_start..period_end, &active_projects)
|
||||
.await
|
||||
.trace_err();
|
||||
period_start = period_end;
|
||||
|
|
Loading…
Reference in a new issue