🎨 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
|
@ -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!()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue