Skip to content

Commit

Permalink
feat(prs): aggregate metrics (#7)
Browse files Browse the repository at this point in the history
* feat: add comments_count

* feat: add commits_count

* feat: add changed_files_count

* feat: add time_to_first_contacted

* feat: add time_to_approved

* feat: add time_to_merged

* chore: update version
  • Loading branch information
hirokisan authored Nov 20, 2024
1 parent 4fcae64 commit b1ce0ae
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "gh-lens"
description = "CLI to analyze your activity on GitHub"
version = "0.0.7"
version = "0.0.8"
edition = "2021"
license = "MIT"

Expand Down
190 changes: 190 additions & 0 deletions src/github/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ pub struct PullRequestsSummary {
end_date: String,

prs_count: i64,
comments_count: PullRequestCommentsCount,
commits_count: PullRequestCommitsCount,
changed_files_count: PullRequestChangedFilesCount,
time_to_first_contacted: PullRequestTimeToFirstContacted,
time_to_approved: PullRequestTimeToApproved,
time_to_merged: PullRequestTimeToMerged,

prs_summaries: Vec<PullRequestSummary>,
}

Expand All @@ -34,6 +41,152 @@ pub struct PullRequestSummary {
merged_at: Option<DateTime>,
}

#[derive(Debug, Serialize)]
pub struct PullRequestCommentsCount {
sum: i64,
average: f64,
}

#[derive(Debug, Serialize)]
pub struct PullRequestCommitsCount {
sum: i64,
average: f64,
}

#[derive(Debug, Serialize)]
pub struct PullRequestChangedFilesCount {
sum: i64,
average: f64,
}

#[derive(Debug, Serialize)]
pub struct PullRequestTimeToFirstContacted {
average: f64, // sec
}

#[derive(Debug, Serialize)]
pub struct PullRequestTimeToApproved {
average: f64, // sec
}

#[derive(Debug, Serialize)]
pub struct PullRequestTimeToMerged {
average: f64, // sec
}

impl PullRequestsSummary {
fn aggregate_summary(&mut self) {
self.aggregate_comments_count();
self.aggregate_commits_count();
self.aggregate_changed_files_count();
self.aggregate_time_to_first_contacted();
self.aggregate_time_to_approved();
self.aggregate_time_to_merged();
}

fn aggregate_comments_count(&mut self) {
self.comments_count.sum = self
.prs_summaries
.iter()
.map(|summary| summary.comments_count)
.sum();
self.comments_count.average = if self.prs_summaries.is_empty() {
0.0
} else {
self.comments_count.sum as f64 / self.prs_summaries.len() as f64
};
}

fn aggregate_commits_count(&mut self) {
self.commits_count.sum = self
.prs_summaries
.iter()
.map(|summary| summary.commits_count)
.sum();
self.commits_count.average = if self.prs_count == 0 {
0.0
} else {
self.commits_count.sum as f64 / self.prs_count as f64
};
}

fn aggregate_changed_files_count(&mut self) {
self.changed_files_count.sum = self
.prs_summaries
.iter()
.map(|summary| summary.changed_files_count)
.sum();
self.changed_files_count.average = if self.prs_count == 0 {
0.0
} else {
self.changed_files_count.sum as f64 / self.prs_count as f64
};
}

fn aggregate_time_to_first_contacted(&mut self) {
let mut count = 0;
let mut total_seconds = 0;
for summary in self.prs_summaries.iter() {
if summary.first_contacted_at.is_none() {
continue;
};
count += 1;
total_seconds += summary
.first_contacted_at
.as_ref()
.unwrap()
.diff_seconds(&summary.created_at);
}
self.time_to_first_contacted.average = if count == 0 {
0.0
} else {
total_seconds as f64 / count as f64
};
}

fn aggregate_time_to_approved(&mut self) {
let mut count = 0;
let mut total_seconds = 0;
for summary in self.prs_summaries.iter() {
if summary.approved_at.is_none() {
continue;
};
count += 1;
total_seconds += summary
.approved_at
.as_ref()
.unwrap()
.diff_seconds(&summary.created_at);
}
self.time_to_approved.average = if count == 0 {
0.0
} else {
total_seconds as f64 / count as f64
};
}

fn aggregate_time_to_merged(&mut self) {
let mut count = 0;
let mut total_seconds = 0;
for summary in self.prs_summaries.iter() {
if summary.merged_at.is_none() {
continue;
};
count += 1;
total_seconds += summary
.merged_at
.as_ref()
.unwrap()
.diff_seconds(&summary.created_at);
}
self.time_to_merged.average = if count == 0 {
0.0
} else {
total_seconds as f64 / count as f64
};
}
}

impl Client {
pub fn new(token: String) -> Self {
let octocrab = octocrab::Octocrab::builder()
Expand Down Expand Up @@ -62,6 +215,21 @@ impl Client {
start_date,
end_date,
prs_count: 0,
comments_count: PullRequestCommentsCount {
sum: 0,
average: 0.0,
},
commits_count: PullRequestCommitsCount {
sum: 0,
average: 0.0,
},
changed_files_count: PullRequestChangedFilesCount {
sum: 0,
average: 0.0,
},
time_to_first_contacted: PullRequestTimeToFirstContacted { average: 0.0 },
time_to_approved: PullRequestTimeToApproved { average: 0.0 },
time_to_merged: PullRequestTimeToMerged { average: 0.0 },
prs_summaries: vec![],
};

Expand Down Expand Up @@ -234,6 +402,8 @@ impl Client {
};
}

summary.aggregate_summary();

summary
}

Expand Down Expand Up @@ -290,6 +460,23 @@ impl Client {
start_date: start_date.clone(),
end_date: end_date.clone(),
prs_count: 0,
comments_count: PullRequestCommentsCount {
sum: 0,
average: 0.0,
},
commits_count: PullRequestCommitsCount {
sum: 0,
average: 0.0,
},
changed_files_count: PullRequestChangedFilesCount {
sum: 0,
average: 0.0,
},
time_to_first_contacted: PullRequestTimeToFirstContacted {
average: 0.0,
},
time_to_approved: PullRequestTimeToApproved { average: 0.0 },
time_to_merged: PullRequestTimeToMerged { average: 0.0 },
prs_summaries: vec![],
});

Expand Down Expand Up @@ -454,6 +641,9 @@ impl Client {
})
});
}
summaries.entry(individual.clone()).and_modify(|summary| {
summary.aggregate_summary();
});
}

if !prs.page_info.has_next_page {
Expand Down
10 changes: 10 additions & 0 deletions src/github/gql/scaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,13 @@ impl TryFrom<DateTime> for chrono::DateTime<chrono::Utc> {
chrono::DateTime::parse_from_rfc3339(value.0.as_str()).map(Into::into)
}
}

impl DateTime {
pub fn diff_seconds(&self, other: &DateTime) -> i64 {
let target = self.0.parse::<chrono::DateTime<chrono::Utc>>().unwrap();
let compare = other.0.parse::<chrono::DateTime<chrono::Utc>>().unwrap();
let duration = target - compare;

duration.num_seconds()
}
}

0 comments on commit b1ce0ae

Please sign in to comment.